Added the word filter store and moved the word filter routes into the route package.
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.
This commit is contained in:
parent
8e81f922ea
commit
01a692ab5b
|
@ -20,7 +20,6 @@ before_script:
|
|||
- ./cc-test-reporter before-build
|
||||
script: ./run-linux-tests
|
||||
after_script:
|
||||
- ls
|
||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
||||
addons:
|
||||
mariadb: '10.0'
|
|
@ -17,7 +17,7 @@ type DefaultAttachmentStore struct {
|
|||
}
|
||||
|
||||
func NewDefaultAttachmentStore() (*DefaultAttachmentStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &DefaultAttachmentStore{
|
||||
add: acc.Insert("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Prepare(),
|
||||
}, acc.FirstError()
|
||||
|
|
|
@ -85,7 +85,7 @@ type DefaultAuth struct {
|
|||
|
||||
// NewDefaultAuth is a factory for spitting out DefaultAuths
|
||||
func NewDefaultAuth() (*DefaultAuth, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &DefaultAuth{
|
||||
login: acc.Select("users").Columns("uid, password, salt").Where("name = ?").Prepare(),
|
||||
logout: acc.Update("users").Set("session = ''").Where("uid = ?").Prepare(),
|
||||
|
|
|
@ -83,8 +83,7 @@ var DbInits dbInits
|
|||
|
||||
func (inits dbInits) Run() error {
|
||||
for _, init := range inits {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
err := init(acc)
|
||||
err := init(qgen.NewAcc())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ type DefaultAgentViewCounter struct {
|
|||
}
|
||||
|
||||
func NewDefaultAgentViewCounter() (*DefaultAgentViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
var agentBuckets = make([]*RWMutexCounterBucket, len(agentMapEnum))
|
||||
for bucketID, _ := range agentBuckets {
|
||||
agentBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
|
|
|
@ -22,7 +22,7 @@ type DefaultForumViewCounter struct {
|
|||
}
|
||||
|
||||
func NewDefaultForumViewCounter() (*DefaultForumViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
counter := &DefaultForumViewCounter{
|
||||
oddMap: make(map[int]*RWMutexCounterBucket),
|
||||
evenMap: make(map[int]*RWMutexCounterBucket),
|
||||
|
|
|
@ -100,7 +100,7 @@ type DefaultLangViewCounter struct {
|
|||
}
|
||||
|
||||
func NewDefaultLangViewCounter() (*DefaultLangViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
|
||||
var langBuckets = make([]*RWMutexCounterBucket, len(langCodes))
|
||||
for bucketID, _ := range langBuckets {
|
||||
|
|
|
@ -18,7 +18,7 @@ type DefaultPostCounter struct {
|
|||
}
|
||||
|
||||
func NewPostCounter() (*DefaultPostCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
counter := &DefaultPostCounter{
|
||||
currentBucket: 0,
|
||||
insert: acc.Insert("postchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
|
||||
|
|
|
@ -30,7 +30,7 @@ type DefaultReferrerTracker struct {
|
|||
}
|
||||
|
||||
func NewDefaultReferrerTracker() (*DefaultReferrerTracker, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
refTracker := &DefaultReferrerTracker{
|
||||
odd: make(map[string]*ReferrerItem),
|
||||
even: make(map[string]*ReferrerItem),
|
||||
|
|
|
@ -13,7 +13,7 @@ type DefaultRouteViewCounter struct {
|
|||
}
|
||||
|
||||
func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum))
|
||||
for bucketID, _ := range routeBuckets {
|
||||
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
|
|
|
@ -12,7 +12,7 @@ type DefaultOSViewCounter struct {
|
|||
}
|
||||
|
||||
func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
var osBuckets = make([]*RWMutexCounterBucket, len(osMapEnum))
|
||||
for bucketID, _ := range osBuckets {
|
||||
osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
|
|
|
@ -18,7 +18,7 @@ type DefaultTopicCounter struct {
|
|||
}
|
||||
|
||||
func NewTopicCounter() (*DefaultTopicCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
counter := &DefaultTopicCounter{
|
||||
currentBucket: 0,
|
||||
insert: acc.Insert("topicchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
|
||||
|
|
|
@ -21,7 +21,7 @@ type DefaultTopicViewCounter struct {
|
|||
}
|
||||
|
||||
func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
counter := &DefaultTopicViewCounter{
|
||||
oddTopics: make(map[int]*RWMutexCounterBucket),
|
||||
evenTopics: make(map[int]*RWMutexCounterBucket),
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package common
|
||||
|
||||
// TODO: Break this file up into multiple files to make it easier to maintain
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
@ -22,6 +23,22 @@ type PluginList map[string]*Plugin
|
|||
// TODO: Have a proper store rather than a map?
|
||||
var Plugins PluginList = make(map[string]*Plugin)
|
||||
|
||||
func (list PluginList) Add(plugin *Plugin) {
|
||||
buildPlugin(plugin)
|
||||
list[plugin.UName] = plugin
|
||||
}
|
||||
|
||||
func buildPlugin(plugin *Plugin) {
|
||||
plugin.Installable = (plugin.Install != nil)
|
||||
/*
|
||||
The Active field should never be altered by a plugin. It's used internally by the software to determine whether an admin has enabled a plugin or not and whether to run it. This will be overwritten by the user's preference.
|
||||
*/
|
||||
plugin.Active = false
|
||||
plugin.Installed = false
|
||||
plugin.Hooks = make(map[string]int)
|
||||
plugin.Data = nil
|
||||
}
|
||||
|
||||
// Hooks with a single argument. Is this redundant? Might be useful for inlining, as variadics aren't inlined? Are closures even inlined to begin with?
|
||||
var Hooks = map[string][]func(interface{}) interface{}{
|
||||
"forums_frow_assign": nil,
|
||||
|
@ -158,7 +175,7 @@ type Plugin struct {
|
|||
Activate func() error
|
||||
Deactivate func() // TODO: We might want to let this return an error?
|
||||
Install func() error
|
||||
Uninstall func() error
|
||||
Uninstall func() error // TODO: I'm not sure uninstall is implemented
|
||||
|
||||
Hooks map[string]int
|
||||
Data interface{} // Usually used for hosting the VMs / reusable elements of non-native plugins
|
||||
|
@ -272,31 +289,6 @@ func (plugins PluginList) Load() error {
|
|||
return rows.Err()
|
||||
}
|
||||
|
||||
func NewPlugin(uname string, name string, author string, url string, settings string, tag string, ptype string, init func() error, activate func() error, deactivate func(), install func() error, uninstall func() error) *Plugin {
|
||||
return &Plugin{
|
||||
UName: uname,
|
||||
Name: name,
|
||||
Author: author,
|
||||
URL: url,
|
||||
Settings: settings,
|
||||
Tag: tag,
|
||||
Type: ptype,
|
||||
Installable: (install != nil),
|
||||
Init: init,
|
||||
Activate: activate,
|
||||
Deactivate: deactivate,
|
||||
Install: install,
|
||||
//Uninstall: uninstall,
|
||||
|
||||
/*
|
||||
The Active field should never be altered by a plugin. It's used internally by the software to determine whether an admin has enabled a plugin or not and whether to run it. This will be overwritten by the user's preference.
|
||||
*/
|
||||
Active: false,
|
||||
Installed: false,
|
||||
Hooks: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
// ? - Is this racey?
|
||||
// TODO: Generate the cases in this switch
|
||||
func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
||||
|
|
|
@ -33,7 +33,7 @@ type MemoryForumPermsStore struct {
|
|||
}
|
||||
|
||||
func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &MemoryForumPermsStore{
|
||||
getByForum: acc.Select("forums_permissions").Columns("gid, permissions").Where("fid = ?").Orderby("gid ASC").Prepare(),
|
||||
getByForumGroup: acc.Select("forums_permissions").Columns("permissions").Where("fid = ? AND gid = ?").Prepare(),
|
||||
|
|
|
@ -70,7 +70,7 @@ type MemoryForumStore struct {
|
|||
|
||||
// NewMemoryForumStore gives you a new instance of MemoryForumStore
|
||||
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
// TODO: Do a proper delete
|
||||
return &MemoryForumStore{
|
||||
get: acc.Select("forums").Columns("name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Where("fid = ?").Prepare(),
|
||||
|
@ -354,7 +354,7 @@ func (mfs *MemoryForumStore) Length() (length int) {
|
|||
return length
|
||||
}
|
||||
|
||||
// TODO: Get the total count of forums in the forum store minus the blanked forums rather than doing a heavy query for this?
|
||||
// TODO: Get the total count of forums in the forum store rather than doing a heavy query for this?
|
||||
// GlobalCount returns the total number of forums
|
||||
func (mfs *MemoryForumStore) GlobalCount() (fcount int) {
|
||||
err := mfs.count.QueryRow().Scan(&fcount)
|
||||
|
|
|
@ -45,7 +45,7 @@ type MemoryGroupStore struct {
|
|||
}
|
||||
|
||||
func NewMemoryGroupStore() (*MemoryGroupStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &MemoryGroupStore{
|
||||
groups: make(map[int]*Group),
|
||||
groupCount: 0,
|
||||
|
|
|
@ -21,7 +21,7 @@ type DefaultIPSearcher struct {
|
|||
|
||||
// NewDefaultIPSearcher gives you a new instance of DefaultIPSearcher
|
||||
func NewDefaultIPSearcher() (*DefaultIPSearcher, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &DefaultIPSearcher{
|
||||
searchUsers: acc.Select("users").Columns("uid").Where("last_ip = ?").Prepare(),
|
||||
searchTopics: acc.Select("users").Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ipaddress = ?")).Prepare(),
|
||||
|
|
|
@ -40,8 +40,7 @@ func (store *DefaultMenuStore) Get(mid int) (*MenuListHolder, error) {
|
|||
}
|
||||
|
||||
func (store *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
err = acc.Select("menu_items").Columns("miid, name, htmlID, cssClass, position, path, aria, tooltip, order, tmplName, guestOnly, memberOnly, staffOnly, adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error {
|
||||
err = qgen.NewAcc().Select("menu_items").Columns("miid, name, htmlID, cssClass, position, path, aria, tooltip, order, tmplName, guestOnly, memberOnly, staffOnly, adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error {
|
||||
var mitem = MenuItem{MenuID: mid}
|
||||
err := rows.Scan(&mitem.ID, &mitem.Name, &mitem.HTMLID, &mitem.CSSClass, &mitem.Position, &mitem.Path, &mitem.Aria, &mitem.Tooltip, &mitem.Order, &mitem.TmplName, &mitem.GuestOnly, &mitem.MemberOnly, &mitem.SuperModOnly, &mitem.AdminOnly)
|
||||
if err != nil {
|
||||
|
|
|
@ -61,12 +61,20 @@ func (js *OttoPluginLang) AddPlugin(meta PluginMeta) (plugin *Plugin, err error)
|
|||
}
|
||||
return nil
|
||||
}
|
||||
var pluginActivate func() error
|
||||
var pluginDeactivate func()
|
||||
var pluginInstall func() error
|
||||
var pluginUninstall func() error
|
||||
|
||||
plugin = NewPlugin(meta.UName, meta.Name, meta.Author, meta.URL, meta.Settings, meta.Tag, "ottojs", pluginInit, pluginActivate, pluginDeactivate, pluginInstall, pluginUninstall)
|
||||
plugin = new(Plugin)
|
||||
plugin.UName = meta.UName
|
||||
plugin.Name = meta.Name
|
||||
plugin.Author = meta.Author
|
||||
plugin.URL = meta.URL
|
||||
plugin.Settings = meta.Settings
|
||||
plugin.Tag = meta.Tag
|
||||
plugin.Type = "ottojs"
|
||||
plugin.Init = pluginInit
|
||||
|
||||
// TODO: Implement plugin life cycle events
|
||||
|
||||
buildPlugin(plugin)
|
||||
|
||||
plugin.Data = script
|
||||
return plugin, nil
|
||||
|
|
|
@ -413,7 +413,11 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
//msg = url_reg.ReplaceAllString(msg,"<a href=\"$2$3//$4\" rel=\"nofollow\">$2$3//$4</a>")
|
||||
|
||||
// Word filter list. E.g. Swear words and other things the admins don't like
|
||||
wordFilters := WordFilterBox.Load().(WordFilterMap)
|
||||
wordFilters, err := WordFilters.GetAll()
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return ""
|
||||
}
|
||||
for _, filter := range wordFilters {
|
||||
msg = strings.Replace(msg, filter.Find, filter.Replacement, -1)
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ type DefaultPollStore struct {
|
|||
}
|
||||
|
||||
func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
if cache == nil {
|
||||
cache = NewNullPollCache()
|
||||
}
|
||||
|
@ -153,8 +153,7 @@ func (store *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err er
|
|||
}
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("polls").Columns("pollID, parentID, parentTable, type, options, votes").Where("pollID IN(" + qlist + ")").Query(pollIDList...)
|
||||
rows, err := qgen.NewAcc().Select("polls").Columns("pollID, parentID, parentTable, type, options, votes").Where("pollID IN(" + qlist + ")").Query(pollIDList...)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
|
|
@ -141,10 +141,10 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
|||
// TODO: GDPR. Add a global control panel notice warning the admins of staff members who don't have 2FA enabled
|
||||
stats.Users = Users.GlobalCount()
|
||||
stats.Groups = Groups.GlobalCount()
|
||||
stats.Forums = Forums.GlobalCount() // TODO: Stop it from showing the blanked forums, do we still have those? I think we removed that
|
||||
stats.Forums = Forums.GlobalCount()
|
||||
stats.Pages = Pages.GlobalCount()
|
||||
stats.Settings = len(header.Settings)
|
||||
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
||||
stats.WordFilters = WordFilters.EstCount()
|
||||
stats.Themes = len(Themes)
|
||||
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ func (store *DefaultStatStore) LookupInt(name string, duration int, unit string)
|
|||
}
|
||||
|
||||
func (store *DefaultStatStore) countTable(table string, duration int, unit string) (stat int, err error) {
|
||||
/*acc := qgen.Builder.Accumulator()
|
||||
counter := acc.Count("replies").DateCutoff("createdAt", 1, "day").Prepare()
|
||||
/*counter := qgen.NewAcc().Count("replies").DateCutoff("createdAt", 1, "day").Prepare()
|
||||
if acc.FirstError() != nil {
|
||||
return 0, acc.FirstError()
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ type DefaultSubscriptionStore struct {
|
|||
}
|
||||
|
||||
func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &DefaultSubscriptionStore{
|
||||
add: acc.Insert("activity_subscriptions").Columns("user, targetID, targetType, level").Fields("?,?,?,2").Prepare(),
|
||||
}, acc.FirstError()
|
||||
|
|
|
@ -93,6 +93,7 @@ func HandleExpiredScheduledGroups() error {
|
|||
// TODO: Use AddScheduledSecondTask
|
||||
// TODO: Be a little more granular with the synchronisation
|
||||
// TODO: Synchronise more things
|
||||
// TODO: Does this even work?
|
||||
func HandleServerSync() error {
|
||||
// We don't want to run any unnecessary queries when there is nothing to synchronise
|
||||
/*if Config.ServerCount > 1 {
|
||||
|
@ -118,7 +119,7 @@ func HandleServerSync() error {
|
|||
log.Print("Unable to reload the settings")
|
||||
return err
|
||||
}
|
||||
err = LoadWordFilters()
|
||||
err = WordFilters.ReloadAll()
|
||||
if err != nil {
|
||||
log.Print("Unable to reload the word filters")
|
||||
return err
|
||||
|
|
|
@ -13,20 +13,18 @@ import (
|
|||
)
|
||||
|
||||
func ThumbTask(thumbChan chan bool) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
for {
|
||||
// Put this goroutine to sleep until we have work to do
|
||||
<-thumbChan
|
||||
|
||||
// TODO: Use a real queue
|
||||
acc := qgen.NewAcc()
|
||||
err := acc.Select("users_avatar_queue").Columns("uid").Limit("0,5").EachInt(func(uid int) error {
|
||||
//log.Print("uid: ", uid)
|
||||
// TODO: Do a bulk user fetch instead?
|
||||
user, err := Users.Get(uid)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
//log.Print("user.RawAvatar: ", user.RawAvatar)
|
||||
|
||||
// Has the avatar been removed or already been processed by the thumbnailer?
|
||||
if len(user.RawAvatar) < 2 || user.RawAvatar[1] == '.' {
|
||||
|
@ -59,6 +57,9 @@ func ThumbTask(thumbChan chan bool) {
|
|||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
if err = acc.FirstError(); err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ type DefaultTopicStore struct {
|
|||
|
||||
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
||||
func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
if cache == nil {
|
||||
cache = NewNullTopicCache()
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ type DefaultUserStore struct {
|
|||
|
||||
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
||||
func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
if cache == nil {
|
||||
cache = NewNullUserCache()
|
||||
}
|
||||
|
@ -154,8 +154,7 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
|
|||
}
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
|
||||
rows, err := qgen.NewAcc().Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
|
|
@ -7,34 +7,55 @@ import (
|
|||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
// TODO: Move some features into methods on this?
|
||||
type WordFilter struct {
|
||||
ID int
|
||||
Find string
|
||||
Replacement string
|
||||
}
|
||||
type WordFilterMap map[int]WordFilter
|
||||
|
||||
var WordFilterBox atomic.Value // An atomic value holding a WordFilterBox
|
||||
var WordFilters WordFilterStore
|
||||
|
||||
type FilterStmts struct {
|
||||
getWordFilters *sql.Stmt
|
||||
type WordFilterStore interface {
|
||||
ReloadAll() error
|
||||
GetAll() (filters map[int]*WordFilter, err error)
|
||||
Create(find string, replacement string) error
|
||||
Delete(id int) error
|
||||
Update(id int, find string, replacement string) error
|
||||
Length() int
|
||||
EstCount() int
|
||||
GlobalCount() (count int)
|
||||
}
|
||||
|
||||
var filterStmts FilterStmts
|
||||
type DefaultWordFilterStore struct {
|
||||
box atomic.Value // An atomic value holding a WordFilterMap
|
||||
|
||||
func init() {
|
||||
WordFilterBox.Store(WordFilterMap(make(map[int]WordFilter)))
|
||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
filterStmts = FilterStmts{
|
||||
getWordFilters: acc.Select("word_filters").Columns("wfid, find, replacement").Prepare(),
|
||||
getAll *sql.Stmt
|
||||
create *sql.Stmt
|
||||
delete *sql.Stmt
|
||||
update *sql.Stmt
|
||||
count *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultWordFilterStore(acc *qgen.Accumulator) (*DefaultWordFilterStore, error) {
|
||||
store := &DefaultWordFilterStore{
|
||||
getAll: acc.Select("word_filters").Columns("wfid, find, replacement").Prepare(),
|
||||
create: acc.Insert("word_filters").Columns("find, replacement").Fields("?,?").Prepare(),
|
||||
delete: acc.Delete("word_filters").Where("wfid = ?").Prepare(),
|
||||
update: acc.Update("word_filters").Set("find = ?, replacement = ?").Where("wfid = ?").Prepare(),
|
||||
count: acc.Count("word_filters").Prepare(),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
// TODO: Should we initialise this elsewhere?
|
||||
if acc.FirstError() == nil {
|
||||
acc.RecordError(store.ReloadAll())
|
||||
}
|
||||
return store, acc.FirstError()
|
||||
}
|
||||
|
||||
func LoadWordFilters() error {
|
||||
var wordFilters = WordFilterMap(make(map[int]WordFilter))
|
||||
filters, err := wordFilters.BypassGetAll()
|
||||
// ReloadAll drops all the items in the memory cache and replaces them with fresh copies from the database
|
||||
func (store *DefaultWordFilterStore) ReloadAll() error {
|
||||
var wordFilters = make(map[int]*WordFilter)
|
||||
filters, err := store.bypassGetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -43,20 +64,20 @@ func LoadWordFilters() error {
|
|||
wordFilters[filter.ID] = filter
|
||||
}
|
||||
|
||||
WordFilterBox.Store(wordFilters)
|
||||
store.box.Store(wordFilters)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Return pointers to word filters intead to save memory?
|
||||
func (wBox WordFilterMap) BypassGetAll() (filters []WordFilter, err error) {
|
||||
rows, err := filterStmts.getWordFilters.Query()
|
||||
// ? - Return pointers to word filters intead to save memory? -- A map is a pointer.
|
||||
func (store *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err error) {
|
||||
rows, err := store.getAll.Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
filter := WordFilter{ID: 0}
|
||||
filter := &WordFilter{ID: 0}
|
||||
err := rows.Scan(&filter.ID, &filter.Find, &filter.Replacement)
|
||||
if err != nil {
|
||||
return filters, err
|
||||
|
@ -66,8 +87,52 @@ func (wBox WordFilterMap) BypassGetAll() (filters []WordFilter, err error) {
|
|||
return filters, rows.Err()
|
||||
}
|
||||
|
||||
func AddWordFilter(id int, find string, replacement string) {
|
||||
wordFilters := WordFilterBox.Load().(WordFilterMap)
|
||||
wordFilters[id] = WordFilter{ID: id, Find: find, Replacement: replacement}
|
||||
WordFilterBox.Store(wordFilters)
|
||||
// GetAll returns all of the word filters in a map. Do note mutate this map (or maps returned from any store not explicitly noted as copies) as multiple threads may be accessing it at once
|
||||
func (store *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err error) {
|
||||
return store.box.Load().(map[int]*WordFilter), nil
|
||||
}
|
||||
|
||||
// Create adds a new word filter to the database and refreshes the memory cache
|
||||
func (store *DefaultWordFilterStore) Create(find string, replacement string) error {
|
||||
_, err := store.create.Exec(find, replacement)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return store.ReloadAll()
|
||||
}
|
||||
|
||||
// Delete removes a word filter from the database and refreshes the memory cache
|
||||
func (store *DefaultWordFilterStore) Delete(id int) error {
|
||||
_, err := store.delete.Exec(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return store.ReloadAll()
|
||||
}
|
||||
|
||||
func (store *DefaultWordFilterStore) Update(id int, find string, replacement string) error {
|
||||
_, err := store.update.Exec(find, replacement, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return store.ReloadAll()
|
||||
}
|
||||
|
||||
// Length gets the number of word filters currently in memory, for the DefaultWordFilterStore, this should be all of them
|
||||
func (store *DefaultWordFilterStore) Length() int {
|
||||
return len(store.box.Load().(map[int]*WordFilter))
|
||||
}
|
||||
|
||||
// EstCount provides the same result as Length(), intended for alternate implementations of WordFilterStore, so that Length is the number of items in cache, if only a subset is held there and EstCount is the total count
|
||||
func (store *DefaultWordFilterStore) EstCount() int {
|
||||
return len(store.box.Load().(map[int]*WordFilter))
|
||||
}
|
||||
|
||||
// GlobalCount gets the total number of word filters directly from the database
|
||||
func (store *DefaultWordFilterStore) GlobalCount() (count int) {
|
||||
err := store.count.QueryRow().Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type SQLGuildStore struct {
|
|||
}
|
||||
|
||||
func NewSQLGuildStore() (*SQLGuildStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
return &SQLGuildStore{
|
||||
get: acc.Select("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Where("guildID = ?").Prepare(),
|
||||
create: acc.Insert("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Fields("?,?,?,?,1,?,1,?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
|
||||
|
|
27
gen_mssql.go
27
gen_mssql.go
|
@ -15,16 +15,13 @@ type Stmts struct {
|
|||
getForumTopics *sql.Stmt
|
||||
addForumPermsToForum *sql.Stmt
|
||||
addTheme *sql.Stmt
|
||||
createWordFilter *sql.Stmt
|
||||
updateTheme *sql.Stmt
|
||||
updateGroupPerms *sql.Stmt
|
||||
updateGroup *sql.Stmt
|
||||
updateEmail *sql.Stmt
|
||||
setTempGroup *sql.Stmt
|
||||
updateWordFilter *sql.Stmt
|
||||
bumpSync *sql.Stmt
|
||||
deleteActivityStreamMatch *sql.Stmt
|
||||
deleteWordFilter *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
getActivityCountByWatcher *sql.Stmt
|
||||
|
@ -88,14 +85,6 @@ func _gen_mssql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing createWordFilter statement.")
|
||||
stmts.createWordFilter, err = db.Prepare("INSERT INTO [word_filters] ([find],[replacement]) VALUES (?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in createWordFilter statement.")
|
||||
log.Print("Bad Query: ","INSERT INTO [word_filters] ([find],[replacement]) VALUES (?,?)")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing updateTheme statement.")
|
||||
stmts.updateTheme, err = db.Prepare("UPDATE [themes] SET [default] = ? WHERE [uname] = ?")
|
||||
if err != nil {
|
||||
|
@ -136,14 +125,6 @@ func _gen_mssql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing updateWordFilter statement.")
|
||||
stmts.updateWordFilter, err = db.Prepare("UPDATE [word_filters] SET [find] = ?,[replacement] = ? WHERE [wfid] = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in updateWordFilter statement.")
|
||||
log.Print("Bad Query: ","UPDATE [word_filters] SET [find] = ?,[replacement] = ? WHERE [wfid] = ?")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing bumpSync statement.")
|
||||
stmts.bumpSync, err = db.Prepare("UPDATE [sync] SET [last_update] = GETUTCDATE()")
|
||||
if err != nil {
|
||||
|
@ -160,13 +141,5 @@ func _gen_mssql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing deleteWordFilter statement.")
|
||||
stmts.deleteWordFilter, err = db.Prepare("DELETE FROM [word_filters] WHERE [wfid] = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in deleteWordFilter statement.")
|
||||
log.Print("Bad Query: ","DELETE FROM [word_filters] WHERE [wfid] = ?")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
24
gen_mysql.go
24
gen_mysql.go
|
@ -17,16 +17,13 @@ type Stmts struct {
|
|||
getForumTopics *sql.Stmt
|
||||
addForumPermsToForum *sql.Stmt
|
||||
addTheme *sql.Stmt
|
||||
createWordFilter *sql.Stmt
|
||||
updateTheme *sql.Stmt
|
||||
updateGroupPerms *sql.Stmt
|
||||
updateGroup *sql.Stmt
|
||||
updateEmail *sql.Stmt
|
||||
setTempGroup *sql.Stmt
|
||||
updateWordFilter *sql.Stmt
|
||||
bumpSync *sql.Stmt
|
||||
deleteActivityStreamMatch *sql.Stmt
|
||||
deleteWordFilter *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
getActivityCountByWatcher *sql.Stmt
|
||||
|
@ -84,13 +81,6 @@ func _gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing createWordFilter statement.")
|
||||
stmts.createWordFilter, err = db.Prepare("INSERT INTO `word_filters`(`find`,`replacement`) VALUES (?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in createWordFilter statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing updateTheme statement.")
|
||||
stmts.updateTheme, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
|
||||
if err != nil {
|
||||
|
@ -126,13 +116,6 @@ func _gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing updateWordFilter statement.")
|
||||
stmts.updateWordFilter, err = db.Prepare("UPDATE `word_filters` SET `find` = ?,`replacement` = ? WHERE `wfid` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in updateWordFilter statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing bumpSync statement.")
|
||||
stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = UTC_TIMESTAMP()")
|
||||
if err != nil {
|
||||
|
@ -147,12 +130,5 @@ func _gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing deleteWordFilter statement.")
|
||||
stmts.deleteWordFilter, err = db.Prepare("DELETE FROM `word_filters` WHERE `wfid` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in deleteWordFilter statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
16
gen_pgsql.go
16
gen_pgsql.go
|
@ -11,13 +11,11 @@ import "./common"
|
|||
type Stmts struct {
|
||||
addForumPermsToForum *sql.Stmt
|
||||
addTheme *sql.Stmt
|
||||
createWordFilter *sql.Stmt
|
||||
updateTheme *sql.Stmt
|
||||
updateGroupPerms *sql.Stmt
|
||||
updateGroup *sql.Stmt
|
||||
updateEmail *sql.Stmt
|
||||
setTempGroup *sql.Stmt
|
||||
updateWordFilter *sql.Stmt
|
||||
bumpSync *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
|
@ -48,13 +46,6 @@ func _gen_pgsql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing createWordFilter statement.")
|
||||
stmts.createWordFilter, err = db.Prepare("INSERT INTO "word_filters"("find","replacement") VALUES (?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in createWordFilter statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing updateTheme statement.")
|
||||
stmts.updateTheme, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
|
||||
if err != nil {
|
||||
|
@ -90,13 +81,6 @@ func _gen_pgsql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing updateWordFilter statement.")
|
||||
stmts.updateWordFilter, err = db.Prepare("UPDATE `word_filters` SET `find` = ?,`replacement` = ? WHERE `wfid` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in updateWordFilter statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing bumpSync statement.")
|
||||
stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = LOCALTIMESTAMP()")
|
||||
if err != nil {
|
||||
|
|
|
@ -46,11 +46,11 @@ var RouteMap = map[string]interface{}{
|
|||
"panel.Settings": panel.Settings,
|
||||
"panel.SettingEdit": panel.SettingEdit,
|
||||
"panel.SettingEditSubmit": panel.SettingEditSubmit,
|
||||
"routePanelWordFilters": routePanelWordFilters,
|
||||
"routePanelWordFiltersCreateSubmit": routePanelWordFiltersCreateSubmit,
|
||||
"routePanelWordFiltersEdit": routePanelWordFiltersEdit,
|
||||
"routePanelWordFiltersEditSubmit": routePanelWordFiltersEditSubmit,
|
||||
"routePanelWordFiltersDeleteSubmit": routePanelWordFiltersDeleteSubmit,
|
||||
"panel.WordFilters": panel.WordFilters,
|
||||
"panel.WordFiltersCreateSubmit": panel.WordFiltersCreateSubmit,
|
||||
"panel.WordFiltersEdit": panel.WordFiltersEdit,
|
||||
"panel.WordFiltersEditSubmit": panel.WordFiltersEditSubmit,
|
||||
"panel.WordFiltersDeleteSubmit": panel.WordFiltersDeleteSubmit,
|
||||
"panel.Pages": panel.Pages,
|
||||
"panel.PagesCreateSubmit": panel.PagesCreateSubmit,
|
||||
"panel.PagesEdit": panel.PagesEdit,
|
||||
|
@ -175,11 +175,11 @@ var routeMapEnum = map[string]int{
|
|||
"panel.Settings": 22,
|
||||
"panel.SettingEdit": 23,
|
||||
"panel.SettingEditSubmit": 24,
|
||||
"routePanelWordFilters": 25,
|
||||
"routePanelWordFiltersCreateSubmit": 26,
|
||||
"routePanelWordFiltersEdit": 27,
|
||||
"routePanelWordFiltersEditSubmit": 28,
|
||||
"routePanelWordFiltersDeleteSubmit": 29,
|
||||
"panel.WordFilters": 25,
|
||||
"panel.WordFiltersCreateSubmit": 26,
|
||||
"panel.WordFiltersEdit": 27,
|
||||
"panel.WordFiltersEditSubmit": 28,
|
||||
"panel.WordFiltersDeleteSubmit": 29,
|
||||
"panel.Pages": 30,
|
||||
"panel.PagesCreateSubmit": 31,
|
||||
"panel.PagesEdit": 32,
|
||||
|
@ -302,11 +302,11 @@ var reverseRouteMapEnum = map[int]string{
|
|||
22: "panel.Settings",
|
||||
23: "panel.SettingEdit",
|
||||
24: "panel.SettingEditSubmit",
|
||||
25: "routePanelWordFilters",
|
||||
26: "routePanelWordFiltersCreateSubmit",
|
||||
27: "routePanelWordFiltersEdit",
|
||||
28: "routePanelWordFiltersEditSubmit",
|
||||
29: "routePanelWordFiltersDeleteSubmit",
|
||||
25: "panel.WordFilters",
|
||||
26: "panel.WordFiltersCreateSubmit",
|
||||
27: "panel.WordFiltersEdit",
|
||||
28: "panel.WordFiltersEditSubmit",
|
||||
29: "panel.WordFiltersDeleteSubmit",
|
||||
30: "panel.Pages",
|
||||
31: "panel.PagesCreateSubmit",
|
||||
32: "panel.PagesEdit",
|
||||
|
@ -1070,7 +1070,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
err = panel.SettingEditSubmit(w,req,user,extraData)
|
||||
case "/panel/settings/word-filters/":
|
||||
counters.RouteViewCounter.Bump(25)
|
||||
err = routePanelWordFilters(w,req,user)
|
||||
err = panel.WordFilters(w,req,user)
|
||||
case "/panel/settings/word-filters/create/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -1079,10 +1079,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(26)
|
||||
err = routePanelWordFiltersCreateSubmit(w,req,user)
|
||||
err = panel.WordFiltersCreateSubmit(w,req,user)
|
||||
case "/panel/settings/word-filters/edit/":
|
||||
counters.RouteViewCounter.Bump(27)
|
||||
err = routePanelWordFiltersEdit(w,req,user,extraData)
|
||||
err = panel.WordFiltersEdit(w,req,user,extraData)
|
||||
case "/panel/settings/word-filters/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -1091,7 +1091,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(28)
|
||||
err = routePanelWordFiltersEditSubmit(w,req,user,extraData)
|
||||
err = panel.WordFiltersEditSubmit(w,req,user,extraData)
|
||||
case "/panel/settings/word-filters/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -1100,7 +1100,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(29)
|
||||
err = routePanelWordFiltersDeleteSubmit(w,req,user,extraData)
|
||||
err = panel.WordFiltersDeleteSubmit(w,req,user,extraData)
|
||||
case "/panel/pages/":
|
||||
err = common.AdminOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
|
|
@ -326,6 +326,7 @@
|
|||
"panel_perms_default":"Default",
|
||||
|
||||
"panel_edit_button_text":"Edit",
|
||||
"panel_update_button_text":"Update",
|
||||
"panel_delete_button_text":"Delete",
|
||||
|
||||
"menu_forums_tooltip":"Forum List",
|
||||
|
|
8
main.go
8
main.go
|
@ -42,7 +42,7 @@ type Globs struct {
|
|||
// Experimenting with a new error package here to try to reduce the amount of debugging we have to do
|
||||
// TODO: Dynamically register these items to avoid maintaining as much code here?
|
||||
func afterDBInit() (err error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
common.Rstore, err = common.NewSQLReplyStore(acc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
|
@ -102,13 +102,11 @@ func afterDBInit() (err error) {
|
|||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
log.Print("Loading the word filters")
|
||||
err = common.LoadWordFilters()
|
||||
log.Print("Initialising the stores")
|
||||
common.WordFilters, err = common.NewDefaultWordFilterStore(acc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
log.Print("Initialising the stores")
|
||||
common.MFAstore, err = common.NewSQLMFAStore(acc)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
|
|
27
misc_test.go
27
misc_test.go
|
@ -980,6 +980,33 @@ func TestPhrases(t *testing.T) {
|
|||
// TODO: Cover the other phrase types, also try switching between languages to see if anything strange happens
|
||||
}
|
||||
|
||||
func TestWordFilters(t *testing.T) {
|
||||
// TODO: Test the word filters and their store
|
||||
expect(t, common.WordFilters.Length() == 0, "Word filter list should be empty")
|
||||
expect(t, common.WordFilters.EstCount() == 0, "Word filter list should be empty")
|
||||
expect(t, common.WordFilters.GlobalCount() == 0, "Word filter list should be empty")
|
||||
filters, err := common.WordFilters.GetAll()
|
||||
expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here
|
||||
expect(t, len(filters) == 0, "Word filter map should be empty")
|
||||
// TODO: Add a test for ParseMessage relating to word filters
|
||||
|
||||
err = common.WordFilters.Create("imbecile", "lovely")
|
||||
expectNilErr(t, err)
|
||||
expect(t, common.WordFilters.Length() == 1, "Word filter list should not be empty")
|
||||
expect(t, common.WordFilters.EstCount() == 1, "Word filter list should not be empty")
|
||||
expect(t, common.WordFilters.GlobalCount() == 1, "Word filter list should not be empty")
|
||||
filters, err = common.WordFilters.GetAll()
|
||||
expectNilErr(t, err)
|
||||
expect(t, len(filters) == 1, "Word filter map should not be empty")
|
||||
filter := filters[1]
|
||||
expect(t, filter.ID == 1, "Word filter ID should be 1")
|
||||
expect(t, filter.Find == "imbecile", "Word filter needle should be imbecile")
|
||||
expect(t, filter.Replacement == "lovely", "Word filter replacement should be lovely")
|
||||
// TODO: Add a test for ParseMessage relating to word filters
|
||||
|
||||
// TODO: Add deletion tests
|
||||
}
|
||||
|
||||
func TestSlugs(t *testing.T) {
|
||||
var res string
|
||||
var msgList []MEPair
|
||||
|
|
129
panel_routes.go
129
panel_routes.go
|
@ -171,135 +171,6 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
return panelRenderTemplate("panel_dashboard", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelWordFilters(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
header.Title = common.GetTitlePhrase("panel_word_filters")
|
||||
|
||||
var filterList = common.WordFilterBox.Load().(common.WordFilterMap)
|
||||
pi := common.PanelPage{&common.BasePanelPage{header, stats, "word-filters", common.ReportForumID}, tList, filterList}
|
||||
return panelRenderTemplate("panel_word_filters", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelWordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
isJs := (r.PostFormValue("js") == "1")
|
||||
|
||||
// ? - We're not doing a full sanitise here, as it would be useful if admins were able to put down rules for replacing things with HTML, etc.
|
||||
find := strings.TrimSpace(r.PostFormValue("find"))
|
||||
if find == "" {
|
||||
return common.LocalErrorJSQ("You need to specify what word you want to match", w, r, user, isJs)
|
||||
}
|
||||
|
||||
// Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement
|
||||
replacement := strings.TrimSpace(r.PostFormValue("replacement"))
|
||||
|
||||
res, err := stmts.createWordFilter.Exec(find, replacement)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
common.AddWordFilter(int(lastID), find, replacement)
|
||||
return panelSuccessRedirect("/panel/settings/word-filters/", w, r, isJs)
|
||||
}
|
||||
|
||||
// TODO: Implement this as a non-JS fallback
|
||||
func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
header.Title = common.GetTitlePhrase("panel_edit_word_filter")
|
||||
_ = wfid
|
||||
|
||||
pi := common.PanelPage{&common.BasePanelPage{header, stats, "word-filters", common.ReportForumID}, tList, nil}
|
||||
return panelRenderTemplate("panel_word_filters_edit", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelWordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Either call it isJs or js rather than flip-flopping back and forth across the routes x.x
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(wfid)
|
||||
if err != nil {
|
||||
return common.LocalErrorJSQ("The word filter ID must be an integer.", w, r, user, isJs)
|
||||
}
|
||||
|
||||
find := strings.TrimSpace(r.PostFormValue("find"))
|
||||
if find == "" {
|
||||
return common.LocalErrorJSQ("You need to specify what word you want to match", w, r, user, isJs)
|
||||
}
|
||||
|
||||
// Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement
|
||||
replacement := strings.TrimSpace(r.PostFormValue("replacement"))
|
||||
|
||||
_, err = stmts.updateWordFilter.Exec(find, replacement, id)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
wordFilters := common.WordFilterBox.Load().(common.WordFilterMap)
|
||||
wordFilters[id] = common.WordFilter{ID: id, Find: find, Replacement: replacement}
|
||||
common.WordFilterBox.Store(wordFilters)
|
||||
|
||||
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func routePanelWordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(wfid)
|
||||
if err != nil {
|
||||
return common.LocalErrorJSQ("The word filter ID must be an integer.", w, r, user, isJs)
|
||||
}
|
||||
|
||||
_, err = stmts.deleteWordFilter.Exec(id)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
wordFilters := common.WordFilterBox.Load().(common.WordFilterMap)
|
||||
delete(wordFilters, id)
|
||||
common.WordFilterBox.Store(wordFilters)
|
||||
|
||||
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
|
|
@ -32,8 +32,7 @@ func execStmt(stmt *sql.Stmt, err error) error {
|
|||
}*/
|
||||
|
||||
func eachUser(handle func(int) error) error {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
err := acc.Select("users").Each(func(rows *sql.Rows) error {
|
||||
err := qgen.NewAcc().Select("users").Each(func(rows *sql.Rows) error {
|
||||
var uid int
|
||||
err := rows.Scan(&uid)
|
||||
if err != nil {
|
||||
|
|
|
@ -4,7 +4,16 @@ package main
|
|||
import "./common"
|
||||
|
||||
func init() {
|
||||
common.Plugins["adventure"] = common.NewPlugin("adventure", "WIP", "Azareal", "http://github.com/Azareal", "", "", "", initAdventure, nil, deactivateAdventure, installAdventure, nil)
|
||||
common.Plugins.Add(&common.Plugin{
|
||||
UName: "adventure",
|
||||
Name: "Adventure",
|
||||
Tag: "WIP",
|
||||
Author: "Azareal",
|
||||
URL: "https://github.com/Azareal",
|
||||
Init: initAdventure,
|
||||
Deactivate: deactivateAdventure,
|
||||
Install: installAdventure,
|
||||
})
|
||||
}
|
||||
|
||||
func initAdventure() error {
|
||||
|
|
|
@ -25,7 +25,7 @@ var bbcodeQuotes *regexp.Regexp
|
|||
var bbcodeCode *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
common.Plugins["bbcode"] = common.NewPlugin("bbcode", "BBCode", "Azareal", "http://github.com/Azareal", "", "", "", initBbcode, nil, deactivateBbcode, nil, nil)
|
||||
common.Plugins.Add(&common.Plugin{UName: "bbcode", Name: "BBCode", Author: "Azareal", URL: "https://github.com/Azareal", Init: initBbcode, Deactivate: deactivateBbcode})
|
||||
}
|
||||
|
||||
func initBbcode() error {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
|
||||
"./common"
|
||||
"./extend/guilds/lib"
|
||||
"./query_gen/lib"
|
||||
|
@ -12,7 +10,7 @@ import (
|
|||
|
||||
// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
||||
func init() {
|
||||
common.Plugins["guilds"] = common.NewPlugin("guilds", "Guilds", "Azareal", "http://github.com/Azareal", "", "", "", initGuilds, nil, deactivateGuilds, installGuilds, nil)
|
||||
common.Plugins.Add(&common.Plugin{UName: "guilds", Name: "Guilds", Author: "Azareal", URL: "https://github.com/Azareal", Init: initGuilds, Deactivate: deactivateGuilds, Install: installGuilds})
|
||||
|
||||
// TODO: Is it possible to avoid doing this when the plugin isn't activated?
|
||||
common.PrebuildTmplList = append(common.PrebuildTmplList, guilds.PrebuildTmplList)
|
||||
|
@ -38,7 +36,7 @@ func initGuilds() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
acc := qgen.NewAcc()
|
||||
|
||||
guilds.ListStmt = acc.Select("guilds").Columns("guildID, name, desc, active, privacy, joinable, owner, memberCount, createdAt, lastUpdateTime").Prepare()
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package main
|
|||
import "./common"
|
||||
|
||||
func init() {
|
||||
common.Plugins["heythere"] = common.NewPlugin("heythere", "Hey There", "Azareal", "http://github.com/Azareal", "", "", "", initHeythere, nil, deactivateHeythere, nil, nil)
|
||||
common.Plugins.Add(&common.Plugin{UName: "heythere", Name: "Hey There", Author: "Azareal", URL: "https://github.com/Azareal", Init: initHeythere, Deactivate: deactivateHeythere})
|
||||
}
|
||||
|
||||
// init_heythere is separate from init() as we don't want the plugin to run if the plugin is disabled
|
||||
|
|
|
@ -20,7 +20,7 @@ var markdownStrikeTagOpen []byte
|
|||
var markdownStrikeTagClose []byte
|
||||
|
||||
func init() {
|
||||
common.Plugins["markdown"] = common.NewPlugin("markdown", "Markdown", "Azareal", "http://github.com/Azareal", "", "", "", initMarkdown, nil, deactivateMarkdown, nil, nil)
|
||||
common.Plugins.Add(&common.Plugin{UName: "markdown", Name: "Markdown", Author: "Azareal", URL: "https://github.com/Azareal", Init: initMarkdown, Deactivate: deactivateMarkdown})
|
||||
}
|
||||
|
||||
func initMarkdown() error {
|
||||
|
|
|
@ -28,7 +28,7 @@ func init() {
|
|||
|
||||
That Uninstallation field which is currently unused is for not only deactivating this plugin, but for purging any data associated with it such a new tables or data produced by the end-user.
|
||||
*/
|
||||
common.Plugins["skeleton"] = common.NewPlugin("skeleton", "Skeleton", "Azareal", "", "", "", "", initSkeleton, activateSkeleton, deactivateSkeleton, nil, nil)
|
||||
common.Plugins.Add(&common.Plugin{UName: "skeleton", Name: "Skeleton", Author: "Azareal", Init: initSkeleton, Activate: activateSkeleton, Deactivate: deactivateSkeleton})
|
||||
}
|
||||
|
||||
func initSkeleton() error { return nil }
|
||||
|
|
|
@ -8,6 +8,11 @@ import (
|
|||
|
||||
var LogPrepares = true
|
||||
|
||||
// So we don't have to do the qgen.Builder.Accumulator() boilerplate all the time
|
||||
func NewAcc() *Accumulator {
|
||||
return Builder.Accumulator()
|
||||
}
|
||||
|
||||
type Accumulator struct {
|
||||
conn *sql.DB
|
||||
adapter Adapter
|
||||
|
@ -35,7 +40,7 @@ func (build *Accumulator) FirstError() error {
|
|||
return build.firstErr
|
||||
}
|
||||
|
||||
func (build *Accumulator) recordError(err error) {
|
||||
func (build *Accumulator) RecordError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
@ -50,11 +55,11 @@ func (build *Accumulator) prepare(res string, err error) *sql.Stmt {
|
|||
log.Print("res: ", res)
|
||||
}
|
||||
if err != nil {
|
||||
build.recordError(err)
|
||||
build.RecordError(err)
|
||||
return nil
|
||||
}
|
||||
stmt, err := build.conn.Prepare(res)
|
||||
build.recordError(err)
|
||||
build.RecordError(err)
|
||||
return stmt
|
||||
}
|
||||
|
||||
|
@ -77,16 +82,16 @@ func (build *Accumulator) exec(query string, args ...interface{}) (res sql.Resul
|
|||
func (build *Accumulator) Tx(handler func(*TransactionBuilder) error) {
|
||||
tx, err := build.conn.Begin()
|
||||
if err != nil {
|
||||
build.recordError(err)
|
||||
build.RecordError(err)
|
||||
return
|
||||
}
|
||||
err = handler(&TransactionBuilder{tx, build.adapter, nil})
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
build.recordError(err)
|
||||
build.RecordError(err)
|
||||
return
|
||||
}
|
||||
build.recordError(tx.Commit())
|
||||
build.RecordError(tx.Commit())
|
||||
}
|
||||
|
||||
func (build *Accumulator) SimpleSelect(table string, columns string, where string, orderby string, limit string) *sql.Stmt {
|
||||
|
@ -140,11 +145,11 @@ func (build *Accumulator) Purge(table string) *sql.Stmt {
|
|||
|
||||
func (build *Accumulator) prepareTx(tx *sql.Tx, res string, err error) (stmt *sql.Stmt) {
|
||||
if err != nil {
|
||||
build.recordError(err)
|
||||
build.RecordError(err)
|
||||
return nil
|
||||
}
|
||||
stmt, err = tx.Prepare(res)
|
||||
build.recordError(err)
|
||||
build.RecordError(err)
|
||||
return stmt
|
||||
}
|
||||
|
||||
|
|
|
@ -284,8 +284,6 @@ func writeInserts(adapter qgen.Adapter) error {
|
|||
|
||||
build.Insert("addTheme").Table("themes").Columns("uname, default").Fields("?,?").Parse()
|
||||
|
||||
build.Insert("createWordFilter").Table("word_filters").Columns("find, replacement").Fields("?,?").Parse()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -302,8 +300,6 @@ func writeUpdates(adapter qgen.Adapter) error {
|
|||
|
||||
build.Update("setTempGroup").Table("users").Set("temp_group = ?").Where("uid = ?").Parse()
|
||||
|
||||
build.Update("updateWordFilter").Table("word_filters").Set("find = ?, replacement = ?").Where("wfid = ?").Parse()
|
||||
|
||||
build.Update("bumpSync").Table("sync").Set("last_update = UTC_TIMESTAMP()").Parse()
|
||||
|
||||
return nil
|
||||
|
@ -317,8 +313,6 @@ func writeDeletes(adapter qgen.Adapter) error {
|
|||
build.Delete("deleteActivityStreamMatch").Table("activity_stream_matches").Where("watcher = ? AND asid = ?").Parse()
|
||||
//build.Delete("deleteActivityStreamMatchesByWatcher").Table("activity_stream_matches").Where("watcher = ?").Parse()
|
||||
|
||||
build.Delete("deleteWordFilter").Table("word_filters").Where("wfid = ?").Parse()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -155,11 +155,11 @@ func buildPanelRoutes() {
|
|||
View("panel.SettingEdit", "/panel/settings/edit/", "extraData"),
|
||||
Action("panel.SettingEditSubmit", "/panel/settings/edit/submit/", "extraData"),
|
||||
|
||||
View("routePanelWordFilters", "/panel/settings/word-filters/"),
|
||||
Action("routePanelWordFiltersCreateSubmit", "/panel/settings/word-filters/create/"),
|
||||
View("routePanelWordFiltersEdit", "/panel/settings/word-filters/edit/", "extraData"),
|
||||
Action("routePanelWordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"),
|
||||
Action("routePanelWordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"),
|
||||
View("panel.WordFilters", "/panel/settings/word-filters/"),
|
||||
Action("panel.WordFiltersCreateSubmit", "/panel/settings/word-filters/create/"),
|
||||
View("panel.WordFiltersEdit", "/panel/settings/word-filters/edit/", "extraData"),
|
||||
Action("panel.WordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"),
|
||||
Action("panel.WordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"),
|
||||
|
||||
View("panel.Pages", "/panel/pages/").Before("AdminOnly"),
|
||||
Action("panel.PagesCreateSubmit", "/panel/pages/create/submit/").Before("AdminOnly"),
|
||||
|
|
|
@ -319,8 +319,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.U
|
|||
}
|
||||
|
||||
// TODO: Add an EmailStore and move this there
|
||||
acc := qgen.Builder.Accumulator()
|
||||
_, err = acc.Insert("emails").Columns("email, uid, validated, token").Fields("?,?,?,?").Exec(email, uid, 0, token)
|
||||
_, err = qgen.NewAcc().Insert("emails").Columns("email, uid, validated, token").Fields("?,?,?,?").Exec(email, uid, 0, token)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -416,8 +415,7 @@ func AccountEditPasswordSubmit(w http.ResponseWriter, r *http.Request, user comm
|
|||
confirmPassword := r.PostFormValue("account-confirm-password")
|
||||
|
||||
// TODO: Use a reusable statement
|
||||
acc := qgen.Builder.Accumulator()
|
||||
err := acc.Select("users").Columns("password, salt").Where("uid = ?").QueryRow(user.ID).Scan(&realPassword, &salt)
|
||||
err := qgen.NewAcc().Select("users").Columns("password, salt").Where("uid = ?").QueryRow(user.ID).Scan(&realPassword, &salt)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.LocalError("Your account no longer exists.", w, r, user)
|
||||
} else if err != nil {
|
||||
|
|
|
@ -133,8 +133,7 @@ func AnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) co
|
|||
|
||||
common.DebugLog("in panel.AnalyticsViews")
|
||||
// TODO: Add some sort of analytics store / iterator?
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -170,9 +169,8 @@ func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsRouteViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Validate the route is valid
|
||||
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(route)
|
||||
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(route)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -211,9 +209,8 @@ func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
agent = common.SanitiseSingleLine(agent)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsAgentViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
rows, err := acc.Select("viewchunks_agents").Columns("count, createdAt").Where("browser = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(agent)
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, createdAt").Where("browser = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(agent)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -256,9 +253,8 @@ func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.Use
|
|||
}
|
||||
|
||||
common.DebugLog("in panel.AnalyticsForumViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
rows, err := acc.Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(fid)
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(fid)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -298,9 +294,8 @@ func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.Us
|
|||
system = common.SanitiseSingleLine(system)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsSystemViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the OS name is valid
|
||||
rows, err := acc.Select("viewchunks_systems").Columns("count, createdAt").Where("system = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system)
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, createdAt").Where("system = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -339,9 +334,8 @@ func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.
|
|||
lang = common.SanitiseSingleLine(lang)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsLanguageViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the language code is valid
|
||||
rows, err := acc.Select("viewchunks_langs").Columns("count, createdAt").Where("lang = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(lang)
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, createdAt").Where("lang = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(lang)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -379,9 +373,8 @@ func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.
|
|||
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsReferrerViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
rows, err := acc.Select("viewchunks_referrers").Columns("count, createdAt").Where("domain = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(domain)
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, createdAt").Where("domain = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(domain)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -414,8 +407,7 @@ func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsTopics")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -450,8 +442,7 @@ func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) co
|
|||
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
common.DebugLog("in panel.AnalyticsPosts")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -505,8 +496,7 @@ func AnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_forums").Columns("count, forum").Where("forum != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, forum").Where("forum != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -548,8 +538,7 @@ func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -582,8 +571,7 @@ func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -621,8 +609,7 @@ func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_systems").Columns("count, system").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, system").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -660,8 +647,7 @@ func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User
|
|||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_langs").Columns("count, lang").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, lang").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -700,8 +686,7 @@ func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User
|
|||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_referrers").Columns("count, domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package panel
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"../../common"
|
||||
)
|
||||
|
||||
//routePanelWordFilter
|
||||
func WordFilters(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
basePage, ferr := buildBasePage(w, r, &user, "word_filters", "word-filters")
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
filterList, err := common.WordFilters.GetAll()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
pi := common.PanelPage{basePage, tList, filterList}
|
||||
return panelRenderTemplate("panel_word_filters", w, r, user, &pi)
|
||||
}
|
||||
|
||||
//routePanelWordFiltersCreateSubmit
|
||||
func WordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
isJs := (r.PostFormValue("js") == "1")
|
||||
|
||||
// ? - We're not doing a full sanitise here, as it would be useful if admins were able to put down rules for replacing things with HTML, etc.
|
||||
find := strings.TrimSpace(r.PostFormValue("find"))
|
||||
if find == "" {
|
||||
return common.LocalErrorJSQ("You need to specify what word you want to match", w, r, user, isJs)
|
||||
}
|
||||
|
||||
// Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement
|
||||
replacement := strings.TrimSpace(r.PostFormValue("replacement"))
|
||||
|
||||
err := common.WordFilters.Create(find, replacement)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
return panelSuccessRedirect("/panel/settings/word-filters/", w, r, isJs)
|
||||
}
|
||||
|
||||
// TODO: Implement this as a non-JS fallback
|
||||
//routePanelWordFiltersEdit
|
||||
func WordFiltersEdit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
basePage, ferr := buildBasePage(w, r, &user, "edit_word_filter", "word-filters")
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
_ = wfid
|
||||
|
||||
pi := common.PanelPage{basePage, tList, nil}
|
||||
return panelRenderTemplate("panel_word_filters_edit", w, r, user, &pi)
|
||||
}
|
||||
|
||||
//routePanelWordFiltersEditSubmit
|
||||
func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Either call it isJs or js rather than flip-flopping back and forth across the routes x.x
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(wfid)
|
||||
if err != nil {
|
||||
return common.LocalErrorJSQ("The word filter ID must be an integer.", w, r, user, isJs)
|
||||
}
|
||||
|
||||
find := strings.TrimSpace(r.PostFormValue("find"))
|
||||
if find == "" {
|
||||
return common.LocalErrorJSQ("You need to specify what word you want to match", w, r, user, isJs)
|
||||
}
|
||||
|
||||
// Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement
|
||||
replacement := strings.TrimSpace(r.PostFormValue("replacement"))
|
||||
|
||||
err = common.WordFilters.Update(id, find, replacement)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
//routePanelWordFiltersDeleteSubmit
|
||||
func WordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(wfid)
|
||||
if err != nil {
|
||||
return common.LocalErrorJSQ("The word filter ID must be an integer.", w, r, user, isJs)
|
||||
}
|
||||
|
||||
err = common.WordFilters.Delete(id)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.LocalErrorJSQ("This word filter doesn't exist", w, r, user, isJs)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
|
@ -83,8 +83,7 @@ func PollResults(w http.ResponseWriter, r *http.Request, user common.User, sPoll
|
|||
}
|
||||
|
||||
// TODO: Abstract this
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("polls_options").Columns("votes").Where("pollID = ?").Orderby("option ASC").Query(poll.ID)
|
||||
rows, err := qgen.NewAcc().Select("polls_options").Columns("votes").Where("pollID = ?").Orderby("option ASC").Query(poll.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
|
||||
// TODO: Add a config setting to disable the liked query for a burst of extra speed
|
||||
if user.Liked > 0 && len(likedQueryList) > 1 /*&& user.LastLiked <= time.Now()*/ {
|
||||
rows, err := qgen.Builder.Accumulator().Select("likes").Columns("targetItem").Where("sentBy = ? AND targetType = 'replies'").In("targetItem", likedQueryList[1:]).Query(user.ID)
|
||||
rows, err := qgen.NewAcc().Select("likes").Columns("targetItem").Where("sentBy = ? AND targetType = 'replies'").In("targetItem", likedQueryList[1:]).Query(user.ID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
<div id="panel_word_filters" class="colstack_item rowlist">
|
||||
{{range .Something}}
|
||||
<div class="rowitem panel_compactrow editable_parent">
|
||||
<a data-field="find" data-type="text" href="/panel/settings/word-filters/edit/{{.ID}}" class="editable_block panel_upshift edit_fields">{{.Find}}</a>
|
||||
<a data-field="find" data-type="text" href="/panel/settings/word-filters/edit/{{.ID}}" class="editable_block panel_upshift edit_fields filter_find">{{.Find}}</a>
|
||||
<span class="itemSeparator"></span>
|
||||
<a data-field="replacement" data-type="text" class="editable_block panel_compacttext">{{.Replacement}}</a>
|
||||
<a data-field="replacement" data-type="text" class="editable_block panel_compacttext filter_replace">{{.Replacement}}</a>
|
||||
<span class="panel_buttons">
|
||||
<a class="panel_tag edit_fields hide_on_edit panel_right_button edit_button" aria-label="{{lang "panel_word_filters_edit_button_aria"}}"></a>
|
||||
<a class="panel_right_button" href="/panel/settings/word-filters/edit/submit/{{.ID}}"><button class='panel_tag submit_edit show_on_edit' type='submit'>{{lang "panel_word_filters_update_button"}}</button></a>
|
||||
<a class="panel_right_button show_on_edit" href="/panel/settings/word-filters/edit/submit/{{.ID}}"><button class='panel_tag submit_edit' type='submit'>{{lang "panel_word_filters_update_button"}}</button></a>
|
||||
<a href="/panel/settings/word-filters/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="panel_tag panel_right_button hide_on_edit delete_button" aria-label="{{lang "panel_word_filters_delete_button_aria"}}"></a>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -414,7 +414,7 @@ h2 {
|
|||
display: block;
|
||||
}
|
||||
|
||||
input, select, button, .formbutton, textarea {
|
||||
input, select, button, .formbutton, .panel_right_button, textarea {
|
||||
border-radius: 3px;
|
||||
background: rgb(90,90,90);
|
||||
color: rgb(200,200,200);
|
||||
|
@ -434,7 +434,7 @@ input {
|
|||
padding-bottom: 3px;
|
||||
font-size: 16px;
|
||||
}
|
||||
button, .formbutton {
|
||||
button, .formbutton, .panel_right_button {
|
||||
background: rgb(110,110,210);
|
||||
color: rgb(250,250,250);
|
||||
font-family: "Segoe UI";
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
padding: 12px;
|
||||
}
|
||||
|
||||
.to_right {
|
||||
.to_right, .panel_buttons {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
|
@ -118,9 +118,18 @@ button, .formbutton {
|
|||
/*background: rgb(110,110,210);
|
||||
color: rgb(250,250,250);*/
|
||||
}
|
||||
button, .formbutton {
|
||||
button, .formbutton, .panel_right_button {
|
||||
background: rgb(100,100,200);
|
||||
}
|
||||
.panel_right_button {
|
||||
margin-left: 2px;
|
||||
}
|
||||
.edit_button:after {
|
||||
content: "{{index .Phrases "panel_edit_button_text"}}";
|
||||
}
|
||||
.delete_button:after {
|
||||
content: "{{index .Phrases "panel_delete_button_text"}}";
|
||||
}
|
||||
#themeSelector select {
|
||||
background: rgb(90,90,90);
|
||||
color: rgb(200,200,200);
|
||||
|
|
Loading…
Reference in New Issue