mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-03-28 22:35:31 +00:00
171 lines
6.7 KiB
Go
171 lines
6.7 KiB
Go
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package moderation
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"slices"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
// ReportStatusType defines the statuses a report (of abusive content) can have.
|
|
type ReportStatusType int //revive:disable-line:exported
|
|
|
|
const (
|
|
// ReportStatusTypeOpen represents the status of open reports that were not yet handled in any way.
|
|
ReportStatusTypeOpen ReportStatusType = iota + 1 // 1
|
|
// ReportStatusTypeHandled represents the status of valid reports, that have been acted upon.
|
|
ReportStatusTypeHandled // 2
|
|
// ReportStatusTypeIgnored represents the status of ignored reports, that were closed without any action.
|
|
ReportStatusTypeIgnored // 3
|
|
)
|
|
|
|
type (
|
|
// AbuseCategoryType defines the categories in which a user can include the reported content.
|
|
AbuseCategoryType int //revive:disable-line:exported
|
|
|
|
// AbuseCategoryItem defines a pair of value and it's corresponding translation key
|
|
// (used when new reports are submitted).
|
|
AbuseCategoryItem struct {
|
|
Value AbuseCategoryType
|
|
TranslationKey string
|
|
}
|
|
)
|
|
|
|
const (
|
|
AbuseCategoryTypeSpam AbuseCategoryType = iota + 1 // 1
|
|
AbuseCategoryTypeMalware // 2
|
|
AbuseCategoryTypeIllegalContent // 3
|
|
AbuseCategoryTypeOtherViolations // 4 (Other violations of platform rules)
|
|
)
|
|
|
|
// GetAbuseCategoriesList returns a list of pairs with the available abuse category types
|
|
// and their corresponding translation keys
|
|
func GetAbuseCategoriesList() []AbuseCategoryItem {
|
|
return []AbuseCategoryItem{
|
|
{AbuseCategoryTypeSpam, "moderation.abuse_category.spam"},
|
|
{AbuseCategoryTypeMalware, "moderation.abuse_category.malware"},
|
|
{AbuseCategoryTypeIllegalContent, "moderation.abuse_category.illegal_content"},
|
|
{AbuseCategoryTypeOtherViolations, "moderation.abuse_category.other_violations"},
|
|
}
|
|
}
|
|
|
|
// ReportedContentType defines the types of content that can be reported
|
|
// (i.e. user/organization profile, repository, issue/pull, comment).
|
|
type ReportedContentType int //revive:disable-line:exported
|
|
|
|
const (
|
|
// ReportedContentTypeUser should be used when reporting abusive users or organizations.
|
|
ReportedContentTypeUser ReportedContentType = iota + 1 // 1
|
|
|
|
// ReportedContentTypeRepository should be used when reporting a repository with abusive content.
|
|
ReportedContentTypeRepository // 2
|
|
|
|
// ReportedContentTypeIssue should be used when reporting an issue or pull request with abusive content.
|
|
ReportedContentTypeIssue // 3
|
|
|
|
// ReportedContentTypeComment should be used when reporting a comment with abusive content.
|
|
ReportedContentTypeComment // 4
|
|
)
|
|
|
|
var allReportedContentTypes = []ReportedContentType{
|
|
ReportedContentTypeUser,
|
|
ReportedContentTypeRepository,
|
|
ReportedContentTypeIssue,
|
|
ReportedContentTypeComment,
|
|
}
|
|
|
|
func (t ReportedContentType) IsValid() bool {
|
|
return slices.Contains(allReportedContentTypes, t)
|
|
}
|
|
|
|
// AbuseReport represents a report of abusive content.
|
|
type AbuseReport struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
Status ReportStatusType `xorm:"NOT NULL DEFAULT 1"`
|
|
// The ID of the user who submitted the report.
|
|
ReporterID int64 `xorm:"NOT NULL"` // index ?!
|
|
// Reported content type: user/organization profile, repository, issue/pull or comment.
|
|
ContentType ReportedContentType `xorm:"INDEX NOT NULL"`
|
|
// The ID of the reported item (based on ContentType: user, repository, issue or comment).
|
|
ContentID int64 `xorm:"NOT NULL"`
|
|
// The abuse category selected by the reporter.
|
|
Category AbuseCategoryType `xorm:"INDEX NOT NULL"`
|
|
// Remarks provided by the reporter.
|
|
Remarks string // TODO: ReporterReparks or Reason
|
|
// The ID of the corresponding shadow-copied content when exists; otherwise null.
|
|
ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"`
|
|
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
|
}
|
|
|
|
var ErrSelfReporting = errors.New("reporting yourself is not allowed")
|
|
|
|
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(AbuseReport))
|
|
}
|
|
|
|
// IsReported reports whether one or more reports were already submitted for contentType and contentID
|
|
// (regardless the status of the reports).
|
|
func IsReported(ctx context.Context, contentType ReportedContentType, contentID int64) bool {
|
|
// TODO: only consider the reports with 'New' status (and adjust the function name)?!
|
|
reported, _ := db.GetEngine(ctx).Exist(&AbuseReport{ContentType: contentType, ContentID: contentID})
|
|
return reported
|
|
}
|
|
|
|
// AlreadyReportedByAndOpen returns if doerID has already submitted a report for contentType and contentID that is still Open.
|
|
func AlreadyReportedByAndOpen(ctx context.Context, doerID int64, contentType ReportedContentType, contentID int64) bool {
|
|
reported, _ := db.GetEngine(ctx).Exist(&AbuseReport{
|
|
Status: ReportStatusTypeOpen,
|
|
ReporterID: doerID,
|
|
ContentType: contentType,
|
|
ContentID: contentID,
|
|
})
|
|
return reported
|
|
}
|
|
|
|
func ReportAbuse(ctx context.Context, report *AbuseReport) error {
|
|
if report.ContentType == ReportedContentTypeUser && report.ReporterID == report.ContentID {
|
|
return ErrSelfReporting
|
|
}
|
|
|
|
if AlreadyReportedByAndOpen(ctx, report.ReporterID, report.ContentType, report.ContentID) {
|
|
log.Warn("Seems that user %d wanted to report again the content with type %d and ID %d; this request will be ignored.", report.ReporterID, report.ContentType, report.ContentID)
|
|
return nil
|
|
}
|
|
|
|
report.Status = ReportStatusTypeOpen
|
|
_, err := db.GetEngine(ctx).Insert(report)
|
|
|
|
return err
|
|
}
|
|
|
|
// MarkAsHandled will change the status to 'Handled' for all reports linked to the same item (user, repository, issue or comment).
|
|
func MarkAsHandled(ctx context.Context, contentType ReportedContentType, contentID int64) error {
|
|
return updateStatus(ctx, contentType, contentID, ReportStatusTypeHandled)
|
|
}
|
|
|
|
// MarkAsIgnored will change the status to 'Ignored' for all reports linked to the same item (user, repository, issue or comment).
|
|
func MarkAsIgnored(ctx context.Context, contentType ReportedContentType, contentID int64) error {
|
|
return updateStatus(ctx, contentType, contentID, ReportStatusTypeIgnored)
|
|
}
|
|
|
|
// updateStatus will set the provided status for any reports linked to the item with the given type and ID.
|
|
func updateStatus(ctx context.Context, contentType ReportedContentType, contentID int64, status ReportStatusType) error {
|
|
_, err := db.GetEngine(ctx).Where(builder.Eq{
|
|
"content_type": contentType,
|
|
"content_id": contentID,
|
|
}).Cols("status").Update(&AbuseReport{Status: status})
|
|
|
|
return err
|
|
}
|