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
|
- ./cc-test-reporter before-build
|
||||||
script: ./run-linux-tests
|
script: ./run-linux-tests
|
||||||
after_script:
|
after_script:
|
||||||
- ls
|
|
||||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
||||||
addons:
|
addons:
|
||||||
mariadb: '10.0'
|
mariadb: '10.0'
|
|
@ -17,7 +17,7 @@ type DefaultAttachmentStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultAttachmentStore() (*DefaultAttachmentStore, error) {
|
func NewDefaultAttachmentStore() (*DefaultAttachmentStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &DefaultAttachmentStore{
|
return &DefaultAttachmentStore{
|
||||||
add: acc.Insert("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Prepare(),
|
add: acc.Insert("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
|
|
|
@ -85,7 +85,7 @@ type DefaultAuth struct {
|
||||||
|
|
||||||
// NewDefaultAuth is a factory for spitting out DefaultAuths
|
// NewDefaultAuth is a factory for spitting out DefaultAuths
|
||||||
func NewDefaultAuth() (*DefaultAuth, error) {
|
func NewDefaultAuth() (*DefaultAuth, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &DefaultAuth{
|
return &DefaultAuth{
|
||||||
login: acc.Select("users").Columns("uid, password, salt").Where("name = ?").Prepare(),
|
login: acc.Select("users").Columns("uid, password, salt").Where("name = ?").Prepare(),
|
||||||
logout: acc.Update("users").Set("session = ''").Where("uid = ?").Prepare(),
|
logout: acc.Update("users").Set("session = ''").Where("uid = ?").Prepare(),
|
||||||
|
|
|
@ -83,8 +83,7 @@ var DbInits dbInits
|
||||||
|
|
||||||
func (inits dbInits) Run() error {
|
func (inits dbInits) Run() error {
|
||||||
for _, init := range inits {
|
for _, init := range inits {
|
||||||
acc := qgen.Builder.Accumulator()
|
err := init(qgen.NewAcc())
|
||||||
err := init(acc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ type DefaultAgentViewCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultAgentViewCounter() (*DefaultAgentViewCounter, error) {
|
func NewDefaultAgentViewCounter() (*DefaultAgentViewCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
var agentBuckets = make([]*RWMutexCounterBucket, len(agentMapEnum))
|
var agentBuckets = make([]*RWMutexCounterBucket, len(agentMapEnum))
|
||||||
for bucketID, _ := range agentBuckets {
|
for bucketID, _ := range agentBuckets {
|
||||||
agentBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
agentBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||||
|
|
|
@ -22,7 +22,7 @@ type DefaultForumViewCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultForumViewCounter() (*DefaultForumViewCounter, error) {
|
func NewDefaultForumViewCounter() (*DefaultForumViewCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
counter := &DefaultForumViewCounter{
|
counter := &DefaultForumViewCounter{
|
||||||
oddMap: make(map[int]*RWMutexCounterBucket),
|
oddMap: make(map[int]*RWMutexCounterBucket),
|
||||||
evenMap: make(map[int]*RWMutexCounterBucket),
|
evenMap: make(map[int]*RWMutexCounterBucket),
|
||||||
|
|
|
@ -100,7 +100,7 @@ type DefaultLangViewCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultLangViewCounter() (*DefaultLangViewCounter, error) {
|
func NewDefaultLangViewCounter() (*DefaultLangViewCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
|
|
||||||
var langBuckets = make([]*RWMutexCounterBucket, len(langCodes))
|
var langBuckets = make([]*RWMutexCounterBucket, len(langCodes))
|
||||||
for bucketID, _ := range langBuckets {
|
for bucketID, _ := range langBuckets {
|
||||||
|
|
|
@ -18,7 +18,7 @@ type DefaultPostCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostCounter() (*DefaultPostCounter, error) {
|
func NewPostCounter() (*DefaultPostCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
counter := &DefaultPostCounter{
|
counter := &DefaultPostCounter{
|
||||||
currentBucket: 0,
|
currentBucket: 0,
|
||||||
insert: acc.Insert("postchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
|
insert: acc.Insert("postchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
|
||||||
|
|
|
@ -30,7 +30,7 @@ type DefaultReferrerTracker struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultReferrerTracker() (*DefaultReferrerTracker, error) {
|
func NewDefaultReferrerTracker() (*DefaultReferrerTracker, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
refTracker := &DefaultReferrerTracker{
|
refTracker := &DefaultReferrerTracker{
|
||||||
odd: make(map[string]*ReferrerItem),
|
odd: make(map[string]*ReferrerItem),
|
||||||
even: make(map[string]*ReferrerItem),
|
even: make(map[string]*ReferrerItem),
|
||||||
|
|
|
@ -13,7 +13,7 @@ type DefaultRouteViewCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum))
|
var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum))
|
||||||
for bucketID, _ := range routeBuckets {
|
for bucketID, _ := range routeBuckets {
|
||||||
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||||
|
|
|
@ -12,7 +12,7 @@ type DefaultOSViewCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
var osBuckets = make([]*RWMutexCounterBucket, len(osMapEnum))
|
var osBuckets = make([]*RWMutexCounterBucket, len(osMapEnum))
|
||||||
for bucketID, _ := range osBuckets {
|
for bucketID, _ := range osBuckets {
|
||||||
osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||||
|
|
|
@ -18,7 +18,7 @@ type DefaultTopicCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTopicCounter() (*DefaultTopicCounter, error) {
|
func NewTopicCounter() (*DefaultTopicCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
counter := &DefaultTopicCounter{
|
counter := &DefaultTopicCounter{
|
||||||
currentBucket: 0,
|
currentBucket: 0,
|
||||||
insert: acc.Insert("topicchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
|
insert: acc.Insert("topicchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
|
||||||
|
|
|
@ -21,7 +21,7 @@ type DefaultTopicViewCounter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
|
func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
counter := &DefaultTopicViewCounter{
|
counter := &DefaultTopicViewCounter{
|
||||||
oddTopics: make(map[int]*RWMutexCounterBucket),
|
oddTopics: make(map[int]*RWMutexCounterBucket),
|
||||||
evenTopics: make(map[int]*RWMutexCounterBucket),
|
evenTopics: make(map[int]*RWMutexCounterBucket),
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package common
|
package common
|
||||||
|
|
||||||
|
// TODO: Break this file up into multiple files to make it easier to maintain
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -22,6 +23,22 @@ type PluginList map[string]*Plugin
|
||||||
// TODO: Have a proper store rather than a map?
|
// TODO: Have a proper store rather than a map?
|
||||||
var Plugins PluginList = make(map[string]*Plugin)
|
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?
|
// 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{}{
|
var Hooks = map[string][]func(interface{}) interface{}{
|
||||||
"forums_frow_assign": nil,
|
"forums_frow_assign": nil,
|
||||||
|
@ -158,7 +175,7 @@ type Plugin struct {
|
||||||
Activate func() error
|
Activate func() error
|
||||||
Deactivate func() // TODO: We might want to let this return an error?
|
Deactivate func() // TODO: We might want to let this return an error?
|
||||||
Install func() error
|
Install func() error
|
||||||
Uninstall func() error
|
Uninstall func() error // TODO: I'm not sure uninstall is implemented
|
||||||
|
|
||||||
Hooks map[string]int
|
Hooks map[string]int
|
||||||
Data interface{} // Usually used for hosting the VMs / reusable elements of non-native plugins
|
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()
|
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?
|
// ? - Is this racey?
|
||||||
// TODO: Generate the cases in this switch
|
// TODO: Generate the cases in this switch
|
||||||
func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ type MemoryForumPermsStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
|
func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &MemoryForumPermsStore{
|
return &MemoryForumPermsStore{
|
||||||
getByForum: acc.Select("forums_permissions").Columns("gid, permissions").Where("fid = ?").Orderby("gid ASC").Prepare(),
|
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(),
|
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
|
// NewMemoryForumStore gives you a new instance of MemoryForumStore
|
||||||
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
// TODO: Do a proper delete
|
// TODO: Do a proper delete
|
||||||
return &MemoryForumStore{
|
return &MemoryForumStore{
|
||||||
get: acc.Select("forums").Columns("name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Where("fid = ?").Prepare(),
|
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
|
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
|
// GlobalCount returns the total number of forums
|
||||||
func (mfs *MemoryForumStore) GlobalCount() (fcount int) {
|
func (mfs *MemoryForumStore) GlobalCount() (fcount int) {
|
||||||
err := mfs.count.QueryRow().Scan(&fcount)
|
err := mfs.count.QueryRow().Scan(&fcount)
|
||||||
|
|
|
@ -45,7 +45,7 @@ type MemoryGroupStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemoryGroupStore() (*MemoryGroupStore, error) {
|
func NewMemoryGroupStore() (*MemoryGroupStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &MemoryGroupStore{
|
return &MemoryGroupStore{
|
||||||
groups: make(map[int]*Group),
|
groups: make(map[int]*Group),
|
||||||
groupCount: 0,
|
groupCount: 0,
|
||||||
|
|
|
@ -21,7 +21,7 @@ type DefaultIPSearcher struct {
|
||||||
|
|
||||||
// NewDefaultIPSearcher gives you a new instance of DefaultIPSearcher
|
// NewDefaultIPSearcher gives you a new instance of DefaultIPSearcher
|
||||||
func NewDefaultIPSearcher() (*DefaultIPSearcher, error) {
|
func NewDefaultIPSearcher() (*DefaultIPSearcher, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &DefaultIPSearcher{
|
return &DefaultIPSearcher{
|
||||||
searchUsers: acc.Select("users").Columns("uid").Where("last_ip = ?").Prepare(),
|
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(),
|
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) {
|
func (store *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
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 {
|
||||||
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 {
|
|
||||||
var mitem = MenuItem{MenuID: mid}
|
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)
|
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 {
|
if err != nil {
|
||||||
|
|
|
@ -61,12 +61,20 @@ func (js *OttoPluginLang) AddPlugin(meta PluginMeta) (plugin *Plugin, err error)
|
||||||
}
|
}
|
||||||
return nil
|
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
|
plugin.Data = script
|
||||||
return plugin, nil
|
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>")
|
//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
|
// 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 {
|
for _, filter := range wordFilters {
|
||||||
msg = strings.Replace(msg, filter.Find, filter.Replacement, -1)
|
msg = strings.Replace(msg, filter.Find, filter.Replacement, -1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ type DefaultPollStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
|
func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
cache = NewNullPollCache()
|
cache = NewNullPollCache()
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,7 @@ func (store *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err er
|
||||||
}
|
}
|
||||||
qlist = qlist[0 : len(qlist)-1]
|
qlist = qlist[0 : len(qlist)-1]
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("polls").Columns("pollID, parentID, parentTable, type, options, votes").Where("pollID IN(" + qlist + ")").Query(pollIDList...)
|
||||||
rows, err := acc.Select("polls").Columns("pollID, parentID, parentTable, type, options, votes").Where("pollID IN(" + qlist + ")").Query(pollIDList...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return list, err
|
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
|
// 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.Users = Users.GlobalCount()
|
||||||
stats.Groups = Groups.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.Pages = Pages.GlobalCount()
|
||||||
stats.Settings = len(header.Settings)
|
stats.Settings = len(header.Settings)
|
||||||
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
stats.WordFilters = WordFilters.EstCount()
|
||||||
stats.Themes = len(Themes)
|
stats.Themes = len(Themes)
|
||||||
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
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) {
|
func (store *DefaultStatStore) countTable(table string, duration int, unit string) (stat int, err error) {
|
||||||
/*acc := qgen.Builder.Accumulator()
|
/*counter := qgen.NewAcc().Count("replies").DateCutoff("createdAt", 1, "day").Prepare()
|
||||||
counter := acc.Count("replies").DateCutoff("createdAt", 1, "day").Prepare()
|
|
||||||
if acc.FirstError() != nil {
|
if acc.FirstError() != nil {
|
||||||
return 0, acc.FirstError()
|
return 0, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ type DefaultSubscriptionStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) {
|
func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &DefaultSubscriptionStore{
|
return &DefaultSubscriptionStore{
|
||||||
add: acc.Insert("activity_subscriptions").Columns("user, targetID, targetType, level").Fields("?,?,?,2").Prepare(),
|
add: acc.Insert("activity_subscriptions").Columns("user, targetID, targetType, level").Fields("?,?,?,2").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
|
|
|
@ -93,6 +93,7 @@ func HandleExpiredScheduledGroups() error {
|
||||||
// TODO: Use AddScheduledSecondTask
|
// TODO: Use AddScheduledSecondTask
|
||||||
// TODO: Be a little more granular with the synchronisation
|
// TODO: Be a little more granular with the synchronisation
|
||||||
// TODO: Synchronise more things
|
// TODO: Synchronise more things
|
||||||
|
// TODO: Does this even work?
|
||||||
func HandleServerSync() error {
|
func HandleServerSync() error {
|
||||||
// We don't want to run any unnecessary queries when there is nothing to synchronise
|
// We don't want to run any unnecessary queries when there is nothing to synchronise
|
||||||
/*if Config.ServerCount > 1 {
|
/*if Config.ServerCount > 1 {
|
||||||
|
@ -118,7 +119,7 @@ func HandleServerSync() error {
|
||||||
log.Print("Unable to reload the settings")
|
log.Print("Unable to reload the settings")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = LoadWordFilters()
|
err = WordFilters.ReloadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Unable to reload the word filters")
|
log.Print("Unable to reload the word filters")
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -13,20 +13,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func ThumbTask(thumbChan chan bool) {
|
func ThumbTask(thumbChan chan bool) {
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
for {
|
for {
|
||||||
// Put this goroutine to sleep until we have work to do
|
// Put this goroutine to sleep until we have work to do
|
||||||
<-thumbChan
|
<-thumbChan
|
||||||
|
|
||||||
// TODO: Use a real queue
|
// TODO: Use a real queue
|
||||||
|
acc := qgen.NewAcc()
|
||||||
err := acc.Select("users_avatar_queue").Columns("uid").Limit("0,5").EachInt(func(uid int) error {
|
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?
|
// TODO: Do a bulk user fetch instead?
|
||||||
user, err := Users.Get(uid)
|
user, err := Users.Get(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
//log.Print("user.RawAvatar: ", user.RawAvatar)
|
|
||||||
|
|
||||||
// Has the avatar been removed or already been processed by the thumbnailer?
|
// Has the avatar been removed or already been processed by the thumbnailer?
|
||||||
if len(user.RawAvatar) < 2 || user.RawAvatar[1] == '.' {
|
if len(user.RawAvatar) < 2 || user.RawAvatar[1] == '.' {
|
||||||
|
@ -59,6 +57,9 @@ func ThumbTask(thumbChan chan bool) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
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
|
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
||||||
func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
cache = NewNullTopicCache()
|
cache = NewNullTopicCache()
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ type DefaultUserStore struct {
|
||||||
|
|
||||||
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
||||||
func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
if cache == nil {
|
if cache == nil {
|
||||||
cache = NewNullUserCache()
|
cache = NewNullUserCache()
|
||||||
}
|
}
|
||||||
|
@ -154,8 +154,7 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
|
||||||
}
|
}
|
||||||
qlist = qlist[0 : len(qlist)-1]
|
qlist = qlist[0 : len(qlist)-1]
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
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...)
|
||||||
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...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,34 +7,55 @@ import (
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Move some features into methods on this?
|
||||||
type WordFilter struct {
|
type WordFilter struct {
|
||||||
ID int
|
ID int
|
||||||
Find string
|
Find string
|
||||||
Replacement string
|
Replacement string
|
||||||
}
|
}
|
||||||
type WordFilterMap map[int]WordFilter
|
|
||||||
|
|
||||||
var WordFilterBox atomic.Value // An atomic value holding a WordFilterBox
|
var WordFilters WordFilterStore
|
||||||
|
|
||||||
type FilterStmts struct {
|
type WordFilterStore interface {
|
||||||
getWordFilters *sql.Stmt
|
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() {
|
getAll *sql.Stmt
|
||||||
WordFilterBox.Store(WordFilterMap(make(map[int]WordFilter)))
|
create *sql.Stmt
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
delete *sql.Stmt
|
||||||
filterStmts = FilterStmts{
|
update *sql.Stmt
|
||||||
getWordFilters: acc.Select("word_filters").Columns("wfid, find, replacement").Prepare(),
|
count *sql.Stmt
|
||||||
}
|
|
||||||
return acc.FirstError()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadWordFilters() error {
|
func NewDefaultWordFilterStore(acc *qgen.Accumulator) (*DefaultWordFilterStore, error) {
|
||||||
var wordFilters = WordFilterMap(make(map[int]WordFilter))
|
store := &DefaultWordFilterStore{
|
||||||
filters, err := wordFilters.BypassGetAll()
|
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(),
|
||||||
|
}
|
||||||
|
// TODO: Should we initialise this elsewhere?
|
||||||
|
if acc.FirstError() == nil {
|
||||||
|
acc.RecordError(store.ReloadAll())
|
||||||
|
}
|
||||||
|
return store, acc.FirstError()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -43,20 +64,20 @@ func LoadWordFilters() error {
|
||||||
wordFilters[filter.ID] = filter
|
wordFilters[filter.ID] = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
WordFilterBox.Store(wordFilters)
|
store.box.Store(wordFilters)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Return pointers to word filters intead to save memory?
|
// ? - Return pointers to word filters intead to save memory? -- A map is a pointer.
|
||||||
func (wBox WordFilterMap) BypassGetAll() (filters []WordFilter, err error) {
|
func (store *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err error) {
|
||||||
rows, err := filterStmts.getWordFilters.Query()
|
rows, err := store.getAll.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
filter := WordFilter{ID: 0}
|
filter := &WordFilter{ID: 0}
|
||||||
err := rows.Scan(&filter.ID, &filter.Find, &filter.Replacement)
|
err := rows.Scan(&filter.ID, &filter.Find, &filter.Replacement)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return filters, err
|
return filters, err
|
||||||
|
@ -66,8 +87,52 @@ func (wBox WordFilterMap) BypassGetAll() (filters []WordFilter, err error) {
|
||||||
return filters, rows.Err()
|
return filters, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddWordFilter(id int, find string, replacement string) {
|
// 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
|
||||||
wordFilters := WordFilterBox.Load().(WordFilterMap)
|
func (store *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err error) {
|
||||||
wordFilters[id] = WordFilter{ID: id, Find: find, Replacement: replacement}
|
return store.box.Load().(map[int]*WordFilter), nil
|
||||||
WordFilterBox.Store(wordFilters)
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func NewSQLGuildStore() (*SQLGuildStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
return &SQLGuildStore{
|
return &SQLGuildStore{
|
||||||
get: acc.Select("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Where("guildID = ?").Prepare(),
|
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(),
|
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
|
getForumTopics *sql.Stmt
|
||||||
addForumPermsToForum *sql.Stmt
|
addForumPermsToForum *sql.Stmt
|
||||||
addTheme *sql.Stmt
|
addTheme *sql.Stmt
|
||||||
createWordFilter *sql.Stmt
|
|
||||||
updateTheme *sql.Stmt
|
updateTheme *sql.Stmt
|
||||||
updateGroupPerms *sql.Stmt
|
updateGroupPerms *sql.Stmt
|
||||||
updateGroup *sql.Stmt
|
updateGroup *sql.Stmt
|
||||||
updateEmail *sql.Stmt
|
updateEmail *sql.Stmt
|
||||||
setTempGroup *sql.Stmt
|
setTempGroup *sql.Stmt
|
||||||
updateWordFilter *sql.Stmt
|
|
||||||
bumpSync *sql.Stmt
|
bumpSync *sql.Stmt
|
||||||
deleteActivityStreamMatch *sql.Stmt
|
deleteActivityStreamMatch *sql.Stmt
|
||||||
deleteWordFilter *sql.Stmt
|
|
||||||
|
|
||||||
getActivityFeedByWatcher *sql.Stmt
|
getActivityFeedByWatcher *sql.Stmt
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
|
@ -88,14 +85,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
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.")
|
common.DebugLog("Preparing updateTheme statement.")
|
||||||
stmts.updateTheme, err = db.Prepare("UPDATE [themes] SET [default] = ? WHERE [uname] = ?")
|
stmts.updateTheme, err = db.Prepare("UPDATE [themes] SET [default] = ? WHERE [uname] = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -136,14 +125,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
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.")
|
common.DebugLog("Preparing bumpSync statement.")
|
||||||
stmts.bumpSync, err = db.Prepare("UPDATE [sync] SET [last_update] = GETUTCDATE()")
|
stmts.bumpSync, err = db.Prepare("UPDATE [sync] SET [last_update] = GETUTCDATE()")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -160,13 +141,5 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
24
gen_mysql.go
24
gen_mysql.go
|
@ -17,16 +17,13 @@ type Stmts struct {
|
||||||
getForumTopics *sql.Stmt
|
getForumTopics *sql.Stmt
|
||||||
addForumPermsToForum *sql.Stmt
|
addForumPermsToForum *sql.Stmt
|
||||||
addTheme *sql.Stmt
|
addTheme *sql.Stmt
|
||||||
createWordFilter *sql.Stmt
|
|
||||||
updateTheme *sql.Stmt
|
updateTheme *sql.Stmt
|
||||||
updateGroupPerms *sql.Stmt
|
updateGroupPerms *sql.Stmt
|
||||||
updateGroup *sql.Stmt
|
updateGroup *sql.Stmt
|
||||||
updateEmail *sql.Stmt
|
updateEmail *sql.Stmt
|
||||||
setTempGroup *sql.Stmt
|
setTempGroup *sql.Stmt
|
||||||
updateWordFilter *sql.Stmt
|
|
||||||
bumpSync *sql.Stmt
|
bumpSync *sql.Stmt
|
||||||
deleteActivityStreamMatch *sql.Stmt
|
deleteActivityStreamMatch *sql.Stmt
|
||||||
deleteWordFilter *sql.Stmt
|
|
||||||
|
|
||||||
getActivityFeedByWatcher *sql.Stmt
|
getActivityFeedByWatcher *sql.Stmt
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
|
@ -84,13 +81,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
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.")
|
common.DebugLog("Preparing updateTheme statement.")
|
||||||
stmts.updateTheme, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
|
stmts.updateTheme, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -126,13 +116,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
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.")
|
common.DebugLog("Preparing bumpSync statement.")
|
||||||
stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = UTC_TIMESTAMP()")
|
stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = UTC_TIMESTAMP()")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -147,12 +130,5 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
16
gen_pgsql.go
16
gen_pgsql.go
|
@ -11,13 +11,11 @@ import "./common"
|
||||||
type Stmts struct {
|
type Stmts struct {
|
||||||
addForumPermsToForum *sql.Stmt
|
addForumPermsToForum *sql.Stmt
|
||||||
addTheme *sql.Stmt
|
addTheme *sql.Stmt
|
||||||
createWordFilter *sql.Stmt
|
|
||||||
updateTheme *sql.Stmt
|
updateTheme *sql.Stmt
|
||||||
updateGroupPerms *sql.Stmt
|
updateGroupPerms *sql.Stmt
|
||||||
updateGroup *sql.Stmt
|
updateGroup *sql.Stmt
|
||||||
updateEmail *sql.Stmt
|
updateEmail *sql.Stmt
|
||||||
setTempGroup *sql.Stmt
|
setTempGroup *sql.Stmt
|
||||||
updateWordFilter *sql.Stmt
|
|
||||||
bumpSync *sql.Stmt
|
bumpSync *sql.Stmt
|
||||||
|
|
||||||
getActivityFeedByWatcher *sql.Stmt
|
getActivityFeedByWatcher *sql.Stmt
|
||||||
|
@ -48,13 +46,6 @@ func _gen_pgsql() (err error) {
|
||||||
return err
|
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.")
|
common.DebugLog("Preparing updateTheme statement.")
|
||||||
stmts.updateTheme, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
|
stmts.updateTheme, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,13 +81,6 @@ func _gen_pgsql() (err error) {
|
||||||
return err
|
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.")
|
common.DebugLog("Preparing bumpSync statement.")
|
||||||
stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = LOCALTIMESTAMP()")
|
stmts.bumpSync, err = db.Prepare("UPDATE `sync` SET `last_update` = LOCALTIMESTAMP()")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -46,11 +46,11 @@ var RouteMap = map[string]interface{}{
|
||||||
"panel.Settings": panel.Settings,
|
"panel.Settings": panel.Settings,
|
||||||
"panel.SettingEdit": panel.SettingEdit,
|
"panel.SettingEdit": panel.SettingEdit,
|
||||||
"panel.SettingEditSubmit": panel.SettingEditSubmit,
|
"panel.SettingEditSubmit": panel.SettingEditSubmit,
|
||||||
"routePanelWordFilters": routePanelWordFilters,
|
"panel.WordFilters": panel.WordFilters,
|
||||||
"routePanelWordFiltersCreateSubmit": routePanelWordFiltersCreateSubmit,
|
"panel.WordFiltersCreateSubmit": panel.WordFiltersCreateSubmit,
|
||||||
"routePanelWordFiltersEdit": routePanelWordFiltersEdit,
|
"panel.WordFiltersEdit": panel.WordFiltersEdit,
|
||||||
"routePanelWordFiltersEditSubmit": routePanelWordFiltersEditSubmit,
|
"panel.WordFiltersEditSubmit": panel.WordFiltersEditSubmit,
|
||||||
"routePanelWordFiltersDeleteSubmit": routePanelWordFiltersDeleteSubmit,
|
"panel.WordFiltersDeleteSubmit": panel.WordFiltersDeleteSubmit,
|
||||||
"panel.Pages": panel.Pages,
|
"panel.Pages": panel.Pages,
|
||||||
"panel.PagesCreateSubmit": panel.PagesCreateSubmit,
|
"panel.PagesCreateSubmit": panel.PagesCreateSubmit,
|
||||||
"panel.PagesEdit": panel.PagesEdit,
|
"panel.PagesEdit": panel.PagesEdit,
|
||||||
|
@ -175,11 +175,11 @@ var routeMapEnum = map[string]int{
|
||||||
"panel.Settings": 22,
|
"panel.Settings": 22,
|
||||||
"panel.SettingEdit": 23,
|
"panel.SettingEdit": 23,
|
||||||
"panel.SettingEditSubmit": 24,
|
"panel.SettingEditSubmit": 24,
|
||||||
"routePanelWordFilters": 25,
|
"panel.WordFilters": 25,
|
||||||
"routePanelWordFiltersCreateSubmit": 26,
|
"panel.WordFiltersCreateSubmit": 26,
|
||||||
"routePanelWordFiltersEdit": 27,
|
"panel.WordFiltersEdit": 27,
|
||||||
"routePanelWordFiltersEditSubmit": 28,
|
"panel.WordFiltersEditSubmit": 28,
|
||||||
"routePanelWordFiltersDeleteSubmit": 29,
|
"panel.WordFiltersDeleteSubmit": 29,
|
||||||
"panel.Pages": 30,
|
"panel.Pages": 30,
|
||||||
"panel.PagesCreateSubmit": 31,
|
"panel.PagesCreateSubmit": 31,
|
||||||
"panel.PagesEdit": 32,
|
"panel.PagesEdit": 32,
|
||||||
|
@ -302,11 +302,11 @@ var reverseRouteMapEnum = map[int]string{
|
||||||
22: "panel.Settings",
|
22: "panel.Settings",
|
||||||
23: "panel.SettingEdit",
|
23: "panel.SettingEdit",
|
||||||
24: "panel.SettingEditSubmit",
|
24: "panel.SettingEditSubmit",
|
||||||
25: "routePanelWordFilters",
|
25: "panel.WordFilters",
|
||||||
26: "routePanelWordFiltersCreateSubmit",
|
26: "panel.WordFiltersCreateSubmit",
|
||||||
27: "routePanelWordFiltersEdit",
|
27: "panel.WordFiltersEdit",
|
||||||
28: "routePanelWordFiltersEditSubmit",
|
28: "panel.WordFiltersEditSubmit",
|
||||||
29: "routePanelWordFiltersDeleteSubmit",
|
29: "panel.WordFiltersDeleteSubmit",
|
||||||
30: "panel.Pages",
|
30: "panel.Pages",
|
||||||
31: "panel.PagesCreateSubmit",
|
31: "panel.PagesCreateSubmit",
|
||||||
32: "panel.PagesEdit",
|
32: "panel.PagesEdit",
|
||||||
|
@ -1070,7 +1070,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
err = panel.SettingEditSubmit(w,req,user,extraData)
|
err = panel.SettingEditSubmit(w,req,user,extraData)
|
||||||
case "/panel/settings/word-filters/":
|
case "/panel/settings/word-filters/":
|
||||||
counters.RouteViewCounter.Bump(25)
|
counters.RouteViewCounter.Bump(25)
|
||||||
err = routePanelWordFilters(w,req,user)
|
err = panel.WordFilters(w,req,user)
|
||||||
case "/panel/settings/word-filters/create/":
|
case "/panel/settings/word-filters/create/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1079,10 +1079,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
counters.RouteViewCounter.Bump(26)
|
counters.RouteViewCounter.Bump(26)
|
||||||
err = routePanelWordFiltersCreateSubmit(w,req,user)
|
err = panel.WordFiltersCreateSubmit(w,req,user)
|
||||||
case "/panel/settings/word-filters/edit/":
|
case "/panel/settings/word-filters/edit/":
|
||||||
counters.RouteViewCounter.Bump(27)
|
counters.RouteViewCounter.Bump(27)
|
||||||
err = routePanelWordFiltersEdit(w,req,user,extraData)
|
err = panel.WordFiltersEdit(w,req,user,extraData)
|
||||||
case "/panel/settings/word-filters/edit/submit/":
|
case "/panel/settings/word-filters/edit/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1091,7 +1091,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
counters.RouteViewCounter.Bump(28)
|
counters.RouteViewCounter.Bump(28)
|
||||||
err = routePanelWordFiltersEditSubmit(w,req,user,extraData)
|
err = panel.WordFiltersEditSubmit(w,req,user,extraData)
|
||||||
case "/panel/settings/word-filters/delete/submit/":
|
case "/panel/settings/word-filters/delete/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1100,7 +1100,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
counters.RouteViewCounter.Bump(29)
|
counters.RouteViewCounter.Bump(29)
|
||||||
err = routePanelWordFiltersDeleteSubmit(w,req,user,extraData)
|
err = panel.WordFiltersDeleteSubmit(w,req,user,extraData)
|
||||||
case "/panel/pages/":
|
case "/panel/pages/":
|
||||||
err = common.AdminOnly(w,req,user)
|
err = common.AdminOnly(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -326,6 +326,7 @@
|
||||||
"panel_perms_default":"Default",
|
"panel_perms_default":"Default",
|
||||||
|
|
||||||
"panel_edit_button_text":"Edit",
|
"panel_edit_button_text":"Edit",
|
||||||
|
"panel_update_button_text":"Update",
|
||||||
"panel_delete_button_text":"Delete",
|
"panel_delete_button_text":"Delete",
|
||||||
|
|
||||||
"menu_forums_tooltip":"Forum List",
|
"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
|
// 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?
|
// TODO: Dynamically register these items to avoid maintaining as much code here?
|
||||||
func afterDBInit() (err error) {
|
func afterDBInit() (err error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.NewAcc()
|
||||||
common.Rstore, err = common.NewSQLReplyStore(acc)
|
common.Rstore, err = common.NewSQLReplyStore(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
@ -102,13 +102,11 @@ func afterDBInit() (err error) {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the word filters")
|
log.Print("Initialising the stores")
|
||||||
err = common.LoadWordFilters()
|
common.WordFilters, err = common.NewDefaultWordFilterStore(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Initialising the stores")
|
|
||||||
common.MFAstore, err = common.NewSQLMFAStore(acc)
|
common.MFAstore, err = common.NewSQLMFAStore(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
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
|
// 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) {
|
func TestSlugs(t *testing.T) {
|
||||||
var res string
|
var res string
|
||||||
var msgList []MEPair
|
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)
|
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 {
|
func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
|
|
|
@ -32,8 +32,7 @@ func execStmt(stmt *sql.Stmt, err error) error {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
func eachUser(handle func(int) error) error {
|
func eachUser(handle func(int) error) error {
|
||||||
acc := qgen.Builder.Accumulator()
|
err := qgen.NewAcc().Select("users").Each(func(rows *sql.Rows) error {
|
||||||
err := acc.Select("users").Each(func(rows *sql.Rows) error {
|
|
||||||
var uid int
|
var uid int
|
||||||
err := rows.Scan(&uid)
|
err := rows.Scan(&uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,7 +4,16 @@ package main
|
||||||
import "./common"
|
import "./common"
|
||||||
|
|
||||||
func init() {
|
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 {
|
func initAdventure() error {
|
||||||
|
|
|
@ -25,7 +25,7 @@ var bbcodeQuotes *regexp.Regexp
|
||||||
var bbcodeCode *regexp.Regexp
|
var bbcodeCode *regexp.Regexp
|
||||||
|
|
||||||
func init() {
|
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 {
|
func initBbcode() error {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"fmt"
|
|
||||||
|
|
||||||
"./common"
|
"./common"
|
||||||
"./extend/guilds/lib"
|
"./extend/guilds/lib"
|
||||||
"./query_gen/lib"
|
"./query_gen/lib"
|
||||||
|
@ -12,7 +10,7 @@ import (
|
||||||
|
|
||||||
// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
||||||
func init() {
|
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?
|
// TODO: Is it possible to avoid doing this when the plugin isn't activated?
|
||||||
common.PrebuildTmplList = append(common.PrebuildTmplList, guilds.PrebuildTmplList)
|
common.PrebuildTmplList = append(common.PrebuildTmplList, guilds.PrebuildTmplList)
|
||||||
|
@ -38,7 +36,7 @@ func initGuilds() (err error) {
|
||||||
return err
|
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()
|
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"
|
import "./common"
|
||||||
|
|
||||||
func init() {
|
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
|
// 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
|
var markdownStrikeTagClose []byte
|
||||||
|
|
||||||
func init() {
|
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 {
|
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.
|
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 }
|
func initSkeleton() error { return nil }
|
||||||
|
|
|
@ -8,6 +8,11 @@ import (
|
||||||
|
|
||||||
var LogPrepares = true
|
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 {
|
type Accumulator struct {
|
||||||
conn *sql.DB
|
conn *sql.DB
|
||||||
adapter Adapter
|
adapter Adapter
|
||||||
|
@ -35,7 +40,7 @@ func (build *Accumulator) FirstError() error {
|
||||||
return build.firstErr
|
return build.firstErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *Accumulator) recordError(err error) {
|
func (build *Accumulator) RecordError(err error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -50,11 +55,11 @@ func (build *Accumulator) prepare(res string, err error) *sql.Stmt {
|
||||||
log.Print("res: ", res)
|
log.Print("res: ", res)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
build.recordError(err)
|
build.RecordError(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stmt, err := build.conn.Prepare(res)
|
stmt, err := build.conn.Prepare(res)
|
||||||
build.recordError(err)
|
build.RecordError(err)
|
||||||
return stmt
|
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) {
|
func (build *Accumulator) Tx(handler func(*TransactionBuilder) error) {
|
||||||
tx, err := build.conn.Begin()
|
tx, err := build.conn.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
build.recordError(err)
|
build.RecordError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = handler(&TransactionBuilder{tx, build.adapter, nil})
|
err = handler(&TransactionBuilder{tx, build.adapter, nil})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
build.recordError(err)
|
build.RecordError(err)
|
||||||
return
|
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 {
|
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) {
|
func (build *Accumulator) prepareTx(tx *sql.Tx, res string, err error) (stmt *sql.Stmt) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
build.recordError(err)
|
build.RecordError(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stmt, err = tx.Prepare(res)
|
stmt, err = tx.Prepare(res)
|
||||||
build.recordError(err)
|
build.RecordError(err)
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,8 +284,6 @@ func writeInserts(adapter qgen.Adapter) error {
|
||||||
|
|
||||||
build.Insert("addTheme").Table("themes").Columns("uname, default").Fields("?,?").Parse()
|
build.Insert("addTheme").Table("themes").Columns("uname, default").Fields("?,?").Parse()
|
||||||
|
|
||||||
build.Insert("createWordFilter").Table("word_filters").Columns("find, replacement").Fields("?,?").Parse()
|
|
||||||
|
|
||||||
return nil
|
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("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()
|
build.Update("bumpSync").Table("sync").Set("last_update = UTC_TIMESTAMP()").Parse()
|
||||||
|
|
||||||
return nil
|
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("deleteActivityStreamMatch").Table("activity_stream_matches").Where("watcher = ? AND asid = ?").Parse()
|
||||||
//build.Delete("deleteActivityStreamMatchesByWatcher").Table("activity_stream_matches").Where("watcher = ?").Parse()
|
//build.Delete("deleteActivityStreamMatchesByWatcher").Table("activity_stream_matches").Where("watcher = ?").Parse()
|
||||||
|
|
||||||
build.Delete("deleteWordFilter").Table("word_filters").Where("wfid = ?").Parse()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,11 +155,11 @@ func buildPanelRoutes() {
|
||||||
View("panel.SettingEdit", "/panel/settings/edit/", "extraData"),
|
View("panel.SettingEdit", "/panel/settings/edit/", "extraData"),
|
||||||
Action("panel.SettingEditSubmit", "/panel/settings/edit/submit/", "extraData"),
|
Action("panel.SettingEditSubmit", "/panel/settings/edit/submit/", "extraData"),
|
||||||
|
|
||||||
View("routePanelWordFilters", "/panel/settings/word-filters/"),
|
View("panel.WordFilters", "/panel/settings/word-filters/"),
|
||||||
Action("routePanelWordFiltersCreateSubmit", "/panel/settings/word-filters/create/"),
|
Action("panel.WordFiltersCreateSubmit", "/panel/settings/word-filters/create/"),
|
||||||
View("routePanelWordFiltersEdit", "/panel/settings/word-filters/edit/", "extraData"),
|
View("panel.WordFiltersEdit", "/panel/settings/word-filters/edit/", "extraData"),
|
||||||
Action("routePanelWordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"),
|
Action("panel.WordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"),
|
||||||
Action("routePanelWordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"),
|
Action("panel.WordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"),
|
||||||
|
|
||||||
View("panel.Pages", "/panel/pages/").Before("AdminOnly"),
|
View("panel.Pages", "/panel/pages/").Before("AdminOnly"),
|
||||||
Action("panel.PagesCreateSubmit", "/panel/pages/create/submit/").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
|
// TODO: Add an EmailStore and move this there
|
||||||
acc := qgen.Builder.Accumulator()
|
_, err = qgen.NewAcc().Insert("emails").Columns("email, uid, validated, token").Fields("?,?,?,?").Exec(email, uid, 0, token)
|
||||||
_, err = acc.Insert("emails").Columns("email, uid, validated, token").Fields("?,?,?,?").Exec(email, uid, 0, token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
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")
|
confirmPassword := r.PostFormValue("account-confirm-password")
|
||||||
|
|
||||||
// TODO: Use a reusable statement
|
// TODO: Use a reusable statement
|
||||||
acc := qgen.Builder.Accumulator()
|
err := qgen.NewAcc().Select("users").Columns("password, salt").Where("uid = ?").QueryRow(user.ID).Scan(&realPassword, &salt)
|
||||||
err := acc.Select("users").Columns("password, salt").Where("uid = ?").QueryRow(user.ID).Scan(&realPassword, &salt)
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return common.LocalError("Your account no longer exists.", w, r, user)
|
return common.LocalError("Your account no longer exists.", w, r, user)
|
||||||
} else if err != nil {
|
} 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")
|
common.DebugLog("in panel.AnalyticsViews")
|
||||||
// TODO: Add some sort of analytics store / iterator?
|
// TODO: Add some sort of analytics store / iterator?
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsRouteViews")
|
common.DebugLog("in panel.AnalyticsRouteViews")
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Validate the route is valid
|
// 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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
agent = common.SanitiseSingleLine(agent)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsAgentViews")
|
common.DebugLog("in panel.AnalyticsAgentViews")
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Verify the agent is valid
|
// 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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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")
|
common.DebugLog("in panel.AnalyticsForumViews")
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Verify the agent is valid
|
// 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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
system = common.SanitiseSingleLine(system)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsSystemViews")
|
common.DebugLog("in panel.AnalyticsSystemViews")
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Verify the OS name is valid
|
// 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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -339,9 +334,8 @@ func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.
|
||||||
lang = common.SanitiseSingleLine(lang)
|
lang = common.SanitiseSingleLine(lang)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsLanguageViews")
|
common.DebugLog("in panel.AnalyticsLanguageViews")
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Verify the language code is valid
|
// 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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsReferrerViews")
|
common.DebugLog("in panel.AnalyticsReferrerViews")
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Verify the agent is valid
|
// 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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsTopics")
|
common.DebugLog("in panel.AnalyticsTopics")
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||||
|
|
||||||
common.DebugLog("in panel.AnalyticsPosts")
|
common.DebugLog("in panel.AnalyticsPosts")
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks_forums").Columns("count, forum").Where("forum != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks_forums").Columns("count, forum").Where("forum != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks_systems").Columns("count, system").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks_systems").Columns("count, system").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks_langs").Columns("count, lang").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks_langs").Columns("count, lang").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("viewchunks_referrers").Columns("count, domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||||
rows, err := acc.Select("viewchunks_referrers").Columns("count, domain").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
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
|
// TODO: Abstract this
|
||||||
acc := qgen.Builder.Accumulator()
|
rows, err := qgen.NewAcc().Select("polls_options").Columns("votes").Where("pollID = ?").Orderby("option ASC").Query(poll.ID)
|
||||||
rows, err := acc.Select("polls_options").Columns("votes").Where("pollID = ?").Orderby("option ASC").Query(poll.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
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
|
// 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()*/ {
|
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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
<div id="panel_word_filters" class="colstack_item rowlist">
|
<div id="panel_word_filters" class="colstack_item rowlist">
|
||||||
{{range .Something}}
|
{{range .Something}}
|
||||||
<div class="rowitem panel_compactrow editable_parent">
|
<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>
|
<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">
|
<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_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>
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -414,7 +414,7 @@ h2 {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, select, button, .formbutton, textarea {
|
input, select, button, .formbutton, .panel_right_button, textarea {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: rgb(90,90,90);
|
background: rgb(90,90,90);
|
||||||
color: rgb(200,200,200);
|
color: rgb(200,200,200);
|
||||||
|
@ -434,7 +434,7 @@ input {
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
button, .formbutton {
|
button, .formbutton, .panel_right_button {
|
||||||
background: rgb(110,110,210);
|
background: rgb(110,110,210);
|
||||||
color: rgb(250,250,250);
|
color: rgb(250,250,250);
|
||||||
font-family: "Segoe UI";
|
font-family: "Segoe UI";
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.to_right {
|
.to_right, .panel_buttons {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,9 +118,18 @@ button, .formbutton {
|
||||||
/*background: rgb(110,110,210);
|
/*background: rgb(110,110,210);
|
||||||
color: rgb(250,250,250);*/
|
color: rgb(250,250,250);*/
|
||||||
}
|
}
|
||||||
button, .formbutton {
|
button, .formbutton, .panel_right_button {
|
||||||
background: rgb(100,100,200);
|
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 {
|
#themeSelector select {
|
||||||
background: rgb(90,90,90);
|
background: rgb(90,90,90);
|
||||||
color: rgb(200,200,200);
|
color: rgb(200,200,200);
|
||||||
|
|
Loading…
Reference in New Issue