package common
import (
"database/sql"
"errors"
"strconv"
qgen "github.com/Azareal/Gosora/query_gen"
)
// TODO: Make the default report forum ID configurable
// TODO: Make sure this constant is used everywhere for the report forum ID
const ReportForumID = 1
var Reports ReportStore
var ErrAlreadyReported = errors.New("This item has already been reported")
// The report system mostly wraps around the topic system for simplicty
type ReportStore interface {
Create(title, content string, user *User, itemType string, itemID int) (int, error)
}
type DefaultReportStore struct {
create *sql.Stmt
exists *sql.Stmt
func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, error) {
t := "topics"
return &DefaultReportStore{
create: acc.Insert(t).Columns("title, content, parsed_content, ip, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?,'report'").Prepare(),
exists: acc.Count(t).Where("data=? AND data!='' AND parentID=?").Prepare(),
}, acc.FirstError()
// ! There's a data race in this. If two users report one item at the exact same time, then both reports will go through
func (s *DefaultReportStore) Create(title, content string, u *User, itemType string, itemID int) (tid int, err error) {
var count int
err = s.exists.QueryRow(itemType+"_"+strconv.Itoa(itemID), ReportForumID).Scan(&count)
if err != nil && err != sql.ErrNoRows {
return 0, err
if count != 0 {
return 0, ErrAlreadyReported
ip := u.GetIP()
if Config.DisablePostIP {
ip = "0"
res, err := s.create.Exec(title, content, ParseMessage(content, 0, "", nil), ip, u.ID, u.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID)
if err != nil {
lastID, err := res.LastInsertId()
tid = int(lastID)
return tid, Forums.AddTopic(tid, u.ID, ReportForumID)