01a692ab5b
Added tests for the word filter store. Added qgen.NewAcc() to reduce the amount of boilerplate needed for creating an accumulator. Exposed the RecordError method on the accumulator. Added an Add method to PluginList and removed AddPlugin() in favour of that. More panel buttons on Nox should be styled now. Added the panel_update_button_text phrase for future use. More errors might be caught in the thumbnailer now. Removed ls from .travis.yml, it was there for debugging Code Climate.
179 lines
5.8 KiB
Go
179 lines
5.8 KiB
Go
/*
|
|
*
|
|
* Gosora Topic Store
|
|
* Copyright Azareal 2017 - 2018
|
|
*
|
|
*/
|
|
package common
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"strings"
|
|
|
|
"../query_gen/lib"
|
|
)
|
|
|
|
// TODO: Add the watchdog goroutine
|
|
// TODO: Add BulkGetMap
|
|
// TODO: Add some sort of update method
|
|
// ? - Should we add stick, lock, unstick, and unlock methods? These might be better on the Topics not the TopicStore
|
|
var Topics TopicStore
|
|
var ErrNoTitle = errors.New("This message is missing a title")
|
|
var ErrLongTitle = errors.New("The title is too long")
|
|
var ErrNoBody = errors.New("This message is missing a body")
|
|
|
|
type TopicStore interface {
|
|
DirtyGet(id int) *Topic
|
|
Get(id int) (*Topic, error)
|
|
BypassGet(id int) (*Topic, error)
|
|
Exists(id int) bool
|
|
Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error)
|
|
AddLastTopic(item *Topic, fid int) error // unimplemented
|
|
Reload(id int) error // Too much SQL logic to move into TopicCache
|
|
// TODO: Implement these two methods
|
|
//Replies(tid int) ([]*Reply, error)
|
|
//RepliesRange(tid int, lower int, higher int) ([]*Reply, error)
|
|
GlobalCount() int
|
|
|
|
SetCache(cache TopicCache)
|
|
GetCache() TopicCache
|
|
}
|
|
|
|
type DefaultTopicStore struct {
|
|
cache TopicCache
|
|
|
|
get *sql.Stmt
|
|
exists *sql.Stmt
|
|
topicCount *sql.Stmt
|
|
create *sql.Stmt
|
|
}
|
|
|
|
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
|
func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
|
acc := qgen.NewAcc()
|
|
if cache == nil {
|
|
cache = NewNullTopicCache()
|
|
}
|
|
return &DefaultTopicStore{
|
|
cache: cache,
|
|
get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, poll, data").Where("tid = ?").Prepare(),
|
|
exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(),
|
|
topicCount: acc.Count("topics").Prepare(),
|
|
create: acc.Insert("topics").Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(),
|
|
}, acc.FirstError()
|
|
}
|
|
|
|
func (mts *DefaultTopicStore) DirtyGet(id int) *Topic {
|
|
topic, err := mts.cache.Get(id)
|
|
if err == nil {
|
|
return topic
|
|
}
|
|
|
|
topic = &Topic{ID: id}
|
|
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
|
|
if err == nil {
|
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
_ = mts.cache.Add(topic)
|
|
return topic
|
|
}
|
|
return BlankTopic()
|
|
}
|
|
|
|
// TODO: Log weird cache errors?
|
|
func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
|
|
topic, err = mts.cache.Get(id)
|
|
if err == nil {
|
|
return topic, nil
|
|
}
|
|
|
|
topic = &Topic{ID: id}
|
|
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
|
|
if err == nil {
|
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
_ = mts.cache.Add(topic)
|
|
}
|
|
return topic, err
|
|
}
|
|
|
|
// BypassGet will always bypass the cache and pull the topic directly from the database
|
|
func (mts *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
|
|
topic := &Topic{ID: id}
|
|
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
|
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
return topic, err
|
|
}
|
|
|
|
func (mts *DefaultTopicStore) Reload(id int) error {
|
|
topic := &Topic{ID: id}
|
|
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
|
|
if err == nil {
|
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
_ = mts.cache.Set(topic)
|
|
} else {
|
|
_ = mts.cache.Remove(id)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (mts *DefaultTopicStore) Exists(id int) bool {
|
|
return mts.exists.QueryRow(id).Scan(&id) == nil
|
|
}
|
|
|
|
func (mts *DefaultTopicStore) Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) {
|
|
if topicName == "" {
|
|
return 0, ErrNoTitle
|
|
}
|
|
// ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory?
|
|
if len(topicName) > Config.MaxTopicTitleLength {
|
|
return 0, ErrLongTitle
|
|
}
|
|
|
|
parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums"))
|
|
if parsedContent == "" {
|
|
return 0, ErrNoBody
|
|
}
|
|
|
|
wcount := WordCount(content)
|
|
// TODO: Move this statement into the topic store
|
|
res, err := mts.create.Exec(fid, topicName, content, parsedContent, uid, ipaddress, wcount, uid)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
lastID, err := res.LastInsertId()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return int(lastID), Forums.AddTopic(int(lastID), uid, fid)
|
|
}
|
|
|
|
// ? - What is this? Do we need it? Should it be in the main store interface?
|
|
func (mts *DefaultTopicStore) AddLastTopic(item *Topic, fid int) error {
|
|
// Coming Soon...
|
|
return nil
|
|
}
|
|
|
|
// GlobalCount returns the total number of topics on these forums
|
|
func (mts *DefaultTopicStore) GlobalCount() (tcount int) {
|
|
err := mts.topicCount.QueryRow().Scan(&tcount)
|
|
if err != nil {
|
|
LogError(err)
|
|
}
|
|
return tcount
|
|
}
|
|
|
|
func (mts *DefaultTopicStore) SetCache(cache TopicCache) {
|
|
mts.cache = cache
|
|
}
|
|
|
|
// TODO: We're temporarily doing this so that you can do tcache != nil in getTopicUser. Refactor it.
|
|
func (mts *DefaultTopicStore) GetCache() TopicCache {
|
|
_, ok := mts.cache.(*NullTopicCache)
|
|
if ok {
|
|
return nil
|
|
}
|
|
return mts.cache
|
|
}
|