WIP forum action code. Currently disabled.
Add Http Conn Count tracking. Move more panel phrases into the panel namespace. Use a string builder in hookgen. Use Countf() in a couple of places to eliminate boilerplate. Reduce prepared stmt boilerplate in forum store with a lambda. Reduce prepared stmt boilerplate in topic.go with a lambda. Reduce prepared stmt boilerplate in group.go with a lambda. Add TestSetCreatedAt method to *Topic. Add DateOlderThanQ method to *accDeleteBuilder and *accUpdateBuilder. Add Stmt method to *accUpdateBuilder and *AccSelectBuilder. Add AccBuilder interface. Shorten variable names. Shorten extractPerm name to ep. Add avatar_visibility setting stub. Implementation coming in a later commit. Don't set an IP for installer generated posts. Add counters_perf_tick_row hook. Add avatar_visibility phrase. Add avatar_visibility_label phrase. Rename forums_no_description to forums_no_desc. Rename panel.forums_create_description_label to panel.forums_create_desc_label. Rename panel.forums_create_description to panel.forums_create_desc. Rename panel_forum_description to panel.forum_desc. Rename panel_forum_description_placeholder to panel.forum_desc_placeholder. Add panel_debug_http_conns_label phrase. Add panel.forum_actions_head phrase. Add panel.forum_actions_create_head phrase. Add panel.forum_action_run_on_topic_creation phrase. Add panel.forum_action_run_days_after_topic_creation phrase. Add panel.forum_action_run_days_after_topic_last_reply phrase. Add panel.forum_action_action phrase. Add panel.forum_action_action_delete phrase. Add panel.forum_action_action_lock phrase. Add panel.forum_action_action_unlock phrase. Add panel.forum_action_action_move phrase. Add panel.forum_action_extra phrase. Add panel.forum_action_create_button phrase. You will need to run the patcher / updater for this commit.
This commit is contained in:
parent
943a684332
commit
c60118e7c4
@ -41,6 +41,7 @@ func AddHooks(add func(name, params, ret, htype string, multiHook, skip bool, de
|
||||
}
|
||||
vhooknoret("router_end", "w http.ResponseWriter,r *http.Request,u *User,prefix string,extraData string")
|
||||
vhooknoret("topic_reply_row_assign", "r *ReplyUser")
|
||||
vhooknoret("counters_perf_tick_row", "low int64,high int64,avg int64")
|
||||
//forums_frow_assign
|
||||
//Hook(name string, data interface{}) interface{}
|
||||
/*hook := func(name, params, ret, pure string) {
|
||||
|
@ -5,12 +5,12 @@ package main // import "github.com/Azareal/Gosora/hook_gen"
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"runtime/debug"
|
||||
|
||||
_ "github.com/Azareal/Gosora/extend"
|
||||
c "github.com/Azareal/Gosora/common"
|
||||
"strings"
|
||||
|
||||
h "github.com/Azareal/Gosora/cmd/common_hook_gen"
|
||||
c "github.com/Azareal/Gosora/common"
|
||||
_ "github.com/Azareal/Gosora/extend"
|
||||
)
|
||||
|
||||
// TODO: Make sure all the errors in this file propagate upwards properly
|
||||
@ -22,7 +22,7 @@ func main() {
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
hooks := make(map[string]int)
|
||||
for _, pl := range c.Plugins {
|
||||
if len(pl.Meta.Hooks) > 0 {
|
||||
@ -45,24 +45,25 @@ func main() {
|
||||
}
|
||||
}
|
||||
log.Printf("hooks: %+v\n", hooks)
|
||||
|
||||
|
||||
imports := []string{"net/http"}
|
||||
hookVars := h.HookVars{imports,nil}
|
||||
hookVars := h.HookVars{imports, nil}
|
||||
var params2sb strings.Builder
|
||||
add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) {
|
||||
var params2 string
|
||||
first := true
|
||||
for _, param := range strings.Split(params,",") {
|
||||
for _, param := range strings.Split(params, ",") {
|
||||
if !first {
|
||||
params2 += ","
|
||||
params2sb.WriteRune(',')
|
||||
}
|
||||
pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param)," "," ")," ")
|
||||
params2 += pspl[0]
|
||||
pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param), " ", " "), " ")
|
||||
params2sb.WriteString(pspl[0])
|
||||
first = false
|
||||
}
|
||||
hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, hooks[name] > 0, multiHook, skip, defaultRet, pure})
|
||||
hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2sb.String(), ret, htype, hooks[name] > 0, multiHook, skip, defaultRet, pure})
|
||||
params2sb.Reset()
|
||||
}
|
||||
|
||||
|
||||
h.AddHooks(add)
|
||||
h.Write(hookVars)
|
||||
log.Println("Successfully generated the hooks")
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ type DefaultActivityStream struct {
|
||||
delete *sql.Stmt
|
||||
deleteByParams *sql.Stmt
|
||||
deleteByParamsExtra *sql.Stmt
|
||||
aidsByParams *sql.Stmt
|
||||
aidsByParams *sql.Stmt
|
||||
aidsByParamsExtra *sql.Stmt
|
||||
count *sql.Stmt
|
||||
}
|
||||
@ -38,7 +38,7 @@ func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, er
|
||||
delete: acc.Delete(as).Where("asid=?").Prepare(),
|
||||
deleteByParams: acc.Delete(as).Where("event=? AND elementID=? AND elementType=?").Prepare(),
|
||||
deleteByParamsExtra: acc.Delete(as).Where("event=? AND elementID=? AND elementType=? AND extra=?").Prepare(),
|
||||
aidsByParams: acc.Select(as).Columns("asid").Where("event=? AND elementID=? AND elementType=?").Prepare(),
|
||||
aidsByParams: acc.Select(as).Columns("asid").Where("event=? AND elementID=? AND elementType=?").Prepare(),
|
||||
aidsByParamsExtra: acc.Select(as).Columns("asid").Where("event=? AND elementID=? AND elementType=? AND extra=?").Prepare(),
|
||||
count: acc.Count(as).Prepare(),
|
||||
}, acc.FirstError()
|
||||
@ -90,16 +90,16 @@ func (s *DefaultActivityStream) AidsByParams(event string, elementID int, elemen
|
||||
return aids, rows.Err()
|
||||
}
|
||||
|
||||
func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, err error) {
|
||||
rows, err := s.aidsByParamsExtra.Query(event, elementID, elementType, extra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, e error) {
|
||||
rows, e := s.aidsByParamsExtra.Query(event, elementID, elementType, extra)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var aid int
|
||||
if err := rows.Scan(&aid); err != nil {
|
||||
return nil, err
|
||||
if e := rows.Scan(&aid); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
aids = append(aids, aid)
|
||||
}
|
||||
@ -109,9 +109,9 @@ func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, e
|
||||
// TODO: Write a test for this
|
||||
// Count returns the total number of activity stream items
|
||||
func (s *DefaultActivityStream) Count() (count int) {
|
||||
err := s.count.QueryRow().Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
e := s.count.QueryRow().Scan(&count)
|
||||
if e != nil {
|
||||
LogError(e)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"database/sql"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -291,3 +293,22 @@ func inqbuildstr(strs []string) ([]interface{}, string) {
|
||||
}
|
||||
return idList, sb.String()
|
||||
}
|
||||
|
||||
var ConnWatch = &ConnWatcher{}
|
||||
|
||||
type ConnWatcher struct {
|
||||
n int64
|
||||
}
|
||||
|
||||
func (cw *ConnWatcher) StateChange(conn net.Conn, state http.ConnState) {
|
||||
switch state {
|
||||
case http.StateNew:
|
||||
atomic.AddInt64(&cw.n, 1)
|
||||
case http.StateHijacked, http.StateClosed:
|
||||
atomic.AddInt64(&cw.n, -1)
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *ConnWatcher) Count() int {
|
||||
return int(atomic.LoadInt64(&cw.n))
|
||||
}
|
||||
|
@ -85,25 +85,19 @@ func (co *Conversation) Posts(offset, itemsPerPage int) (posts []*ConversationPo
|
||||
}
|
||||
|
||||
func (co *Conversation) PostsCount() (count int) {
|
||||
err := convoStmts.countPosts.QueryRow(co.ID).Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return count
|
||||
return Countf(convoStmts.countPosts, co.ID)
|
||||
}
|
||||
|
||||
func (co *Conversation) Uids() (ids []int, err error) {
|
||||
rows, err := convoStmts.getUsers.Query(co.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rows, e := convoStmts.getUsers.Query(co.ID)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var id int
|
||||
err := rows.Scan(&id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if e := rows.Scan(&id); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
@ -111,12 +105,7 @@ func (co *Conversation) Uids() (ids []int, err error) {
|
||||
}
|
||||
|
||||
func (co *Conversation) Has(uid int) (in bool) {
|
||||
var count int
|
||||
err := convoStmts.has.QueryRow(uid, co.ID).Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return count > 0
|
||||
return Countf(convoStmts.has, uid, co.ID) > 0
|
||||
}
|
||||
|
||||
func (co *Conversation) Update() error {
|
||||
@ -247,7 +236,7 @@ func (s *DefaultConversationStore) GetUserExtra(uid, offset int) (cos []*Convers
|
||||
cmap[co.ID] = &ConversationExtra{co, nil}
|
||||
}
|
||||
|
||||
// TODO: Add a function for the q stuff
|
||||
// TODO: Use inqbuild for this or a similar function
|
||||
var q string
|
||||
idList := make([]interface{}, len(raw))
|
||||
for i, co := range raw {
|
||||
|
@ -51,21 +51,22 @@ func (co *DefaultPerfCounter) Tick() error {
|
||||
b.Unlock()
|
||||
return c
|
||||
}
|
||||
var low int64
|
||||
hTbl := c.GetHookTable()
|
||||
for _, b := range co.buckets {
|
||||
var low int64
|
||||
b.low.Lock()
|
||||
low = b.low.counter
|
||||
b.low.counter = math.MaxInt64
|
||||
low, b.low.counter = b.low.counter, math.MaxInt64
|
||||
b.low.Unlock()
|
||||
if low == math.MaxInt64 {
|
||||
low = 0
|
||||
}
|
||||
high := getCounter(b.high)
|
||||
avg := getCounter(b.avg)
|
||||
c.H_counters_perf_tick_row_hook(hTbl, low, high, avg)
|
||||
|
||||
err := co.insertChunk(low, high, avg) // TODO: Bulk insert for speed?
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), "perf counter")
|
||||
e := co.insertChunk(low, high, avg) // TODO: Bulk insert for speed?
|
||||
if e != nil {
|
||||
return errors.Wrap(errors.WithStack(e), "perf counter")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -76,8 +77,8 @@ func (co *DefaultPerfCounter) insertChunk(low, high, avg int64) error {
|
||||
return nil
|
||||
}
|
||||
c.DebugLogf("Inserting a pchunk with low %d, high %d, avg %d", low, high, avg)
|
||||
_, err := co.insert.Exec(low, high, avg)
|
||||
return err
|
||||
_, e := co.insert.Exec(low, high, avg)
|
||||
return e
|
||||
}
|
||||
|
||||
func (co *DefaultPerfCounter) Push(dur time.Duration /*,_ bool*/) {
|
||||
|
@ -126,6 +126,8 @@ var hookTable = &HookTable{
|
||||
|
||||
"tasks_tick_topic_list": nil,
|
||||
"tasks_tick_widget_wol": nil,
|
||||
|
||||
"counters_perf_tick_row": nil,
|
||||
},
|
||||
map[string][]func(string) string{
|
||||
"preparse_preassign": nil,
|
||||
|
399
common/forum_actions.go
Normal file
399
common/forum_actions.go
Normal file
@ -0,0 +1,399 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
qgen "github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
||||
var ForumActionStore ForumActionStoreInt
|
||||
|
||||
//var ForumActionRunnableStore ForumActionRunnableStoreInt
|
||||
|
||||
const (
|
||||
ForumActionDelete = iota
|
||||
ForumActionLock
|
||||
ForumActionUnlock
|
||||
ForumActionMove
|
||||
)
|
||||
|
||||
func ConvStringToAct(s string) int {
|
||||
switch s {
|
||||
case "delete":
|
||||
return ForumActionDelete
|
||||
case "lock":
|
||||
return ForumActionLock
|
||||
case "unlock":
|
||||
return ForumActionUnlock
|
||||
case "move":
|
||||
return ForumActionMove
|
||||
}
|
||||
return -1
|
||||
}
|
||||
func ConvActToString(a int) string {
|
||||
switch a {
|
||||
case ForumActionDelete:
|
||||
return "delete"
|
||||
case ForumActionLock:
|
||||
return "lock"
|
||||
case ForumActionUnlock:
|
||||
return "unlock"
|
||||
case ForumActionMove:
|
||||
return "move"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var forumActionStmts ForumActionStmts
|
||||
|
||||
type ForumActionStmts struct {
|
||||
get1 *sql.Stmt
|
||||
get2 *sql.Stmt
|
||||
lock1 *sql.Stmt
|
||||
lock2 *sql.Stmt
|
||||
unlock1 *sql.Stmt
|
||||
unlock2 *sql.Stmt
|
||||
}
|
||||
|
||||
type ForumAction struct {
|
||||
ID int
|
||||
Forum int
|
||||
RunOnTopicCreation bool
|
||||
RunDaysAfterTopicCreation int
|
||||
RunDaysAfterTopicLastReply int
|
||||
Action int
|
||||
Extra string
|
||||
}
|
||||
|
||||
func init() {
|
||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
t := "topics"
|
||||
forumActionStmts = ForumActionStmts{
|
||||
get1: acc.Select(t).Cols("tid,createdBy,poll").Where("parentID=?").DateOlderThanQ("createdAt", "day").Stmt(),
|
||||
get2: acc.Select(t).Cols("tid,createdBy,poll").Where("parentID=?").DateOlderThanQ("lastReplyAt", "day").Stmt(),
|
||||
|
||||
/*lock1: acc.Update(t).Set("is_closed=1").Where("parentID=?").DateOlderThanQ("createdAt", "day").Stmt(),
|
||||
lock2: acc.Update(t).Set("is_closed=1").Where("parentID=?").DateOlderThanQ("lastReplyAt", "day").Stmt(),
|
||||
unlock1: acc.Update(t).Set("is_closed=0").Where("parentID=?").DateOlderThanQ("createdAt", "day").Stmt(),
|
||||
unlock2: acc.Update(t).Set("is_closed=0").Where("parentID=?").DateOlderThanQ("lastReplyAt", "day").Stmt(),*/
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
}
|
||||
|
||||
func (a *ForumAction) Run() error {
|
||||
if a.RunDaysAfterTopicCreation > 0 {
|
||||
if e := a.runDaysAfterTopicCreation(); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
if a.RunDaysAfterTopicLastReply > 0 {
|
||||
if e := a.runDaysAfterTopicLastReply(); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ForumAction) runQ(stmt *sql.Stmt, days int, f func(t *Topic) error) error {
|
||||
rows, e := stmt.Query(days, a.Forum)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
// TODO: Decouple this
|
||||
t := &Topic{ParentID: a.Forum}
|
||||
if e := rows.Scan(&t.ID, &t.CreatedBy, &t.Poll); e != nil {
|
||||
return e
|
||||
}
|
||||
if e = f(t); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (a *ForumAction) runDaysAfterTopicCreation() (e error) {
|
||||
switch a.Action {
|
||||
case ForumActionDelete:
|
||||
// TODO: Bulk delete?
|
||||
e = a.runQ(forumActionStmts.get1, a.RunDaysAfterTopicCreation, func(t *Topic) error {
|
||||
return t.Delete()
|
||||
})
|
||||
case ForumActionLock:
|
||||
/*_, e := forumActionStmts.lock1.Exec(a.Forum)
|
||||
if e != nil {
|
||||
return e
|
||||
}*/
|
||||
// TODO: Bulk lock? Lock and get resultset of changed topics somehow?
|
||||
fmt.Println("ForumActionLock")
|
||||
e = a.runQ(forumActionStmts.get1, a.RunDaysAfterTopicCreation, func(t *Topic) error {
|
||||
fmt.Printf("t: %+v\n", t)
|
||||
return t.Lock()
|
||||
})
|
||||
case ForumActionUnlock:
|
||||
// TODO: Bulk unlock? Unlock and get resultset of changed topics somehow?
|
||||
e = a.runQ(forumActionStmts.get1, a.RunDaysAfterTopicCreation, func(t *Topic) error {
|
||||
return t.Unlock()
|
||||
})
|
||||
case ForumActionMove:
|
||||
destForum, e := strconv.Atoi(a.Extra)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
e = a.runQ(forumActionStmts.get1, a.RunDaysAfterTopicCreation, func(t *Topic) error {
|
||||
return t.MoveTo(destForum)
|
||||
})
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (a *ForumAction) runDaysAfterTopicLastReply() (e error) {
|
||||
switch a.Action {
|
||||
case ForumActionDelete:
|
||||
e = a.runQ(forumActionStmts.get2, a.RunDaysAfterTopicLastReply, func(t *Topic) error {
|
||||
return t.Delete()
|
||||
})
|
||||
case ForumActionLock:
|
||||
// TODO: Bulk lock? Lock and get resultset of changed topics somehow?
|
||||
e = a.runQ(forumActionStmts.get2, a.RunDaysAfterTopicLastReply, func(t *Topic) error {
|
||||
return t.Lock()
|
||||
})
|
||||
case ForumActionUnlock:
|
||||
// TODO: Bulk unlock? Unlock and get resultset of changed topics somehow?
|
||||
e = a.runQ(forumActionStmts.get2, a.RunDaysAfterTopicLastReply, func(t *Topic) error {
|
||||
return t.Unlock()
|
||||
})
|
||||
case ForumActionMove:
|
||||
destForum, e := strconv.Atoi(a.Extra)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
e = a.runQ(forumActionStmts.get2, a.RunDaysAfterTopicLastReply, func(t *Topic) error {
|
||||
return t.MoveTo(destForum)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ForumAction) TopicCreation(tid int) error {
|
||||
if !a.RunOnTopicCreation {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ForumActionStoreInt interface {
|
||||
Get(faid int) (*ForumAction, error)
|
||||
GetInForum(fid int) ([]*ForumAction, error)
|
||||
GetAll() ([]*ForumAction, error)
|
||||
GetNewTopicActions(fid int) ([]*ForumAction, error)
|
||||
|
||||
Add(fa *ForumAction) (int, error)
|
||||
Delete(faid int) error
|
||||
Exists(faid int) bool
|
||||
Count() int
|
||||
CountInForum(fid int) int
|
||||
|
||||
DailyTick() error
|
||||
}
|
||||
|
||||
type DefaultForumActionStore struct {
|
||||
get *sql.Stmt
|
||||
getInForum *sql.Stmt
|
||||
getAll *sql.Stmt
|
||||
getNewTopicActions *sql.Stmt
|
||||
|
||||
add *sql.Stmt
|
||||
delete *sql.Stmt
|
||||
exists *sql.Stmt
|
||||
count *sql.Stmt
|
||||
countInForum *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultForumActionStore(acc *qgen.Accumulator) (*DefaultForumActionStore, error) {
|
||||
fa := "forums_actions"
|
||||
allCols := "faid,fid,runOnTopicCreation,runDaysAfterTopicCreation,runDaysAfterTopicLastReply,action,extra"
|
||||
return &DefaultForumActionStore{
|
||||
get: acc.Select(fa).Columns("fid,runOnTopicCreation,runDaysAfterTopicCreation,runDaysAfterTopicLastReply,action,extra").Where("faid=?").Prepare(),
|
||||
getInForum: acc.Select(fa).Columns("faid,runOnTopicCreation,runDaysAfterTopicCreation,runDaysAfterTopicLastReply,action,extra").Where("fid=?").Prepare(),
|
||||
getAll: acc.Select(fa).Columns(allCols).Prepare(),
|
||||
getNewTopicActions: acc.Select(fa).Columns(allCols).Where("fid=? AND runOnTopicCreation=1").Prepare(),
|
||||
|
||||
add: acc.Insert(fa).Columns("fid,runOnTopicCreation,runDaysAfterTopicCreation,runDaysAfterTopicLastReply,action,extra").Fields("?,?,?,?,?,?").Prepare(),
|
||||
delete: acc.Delete(fa).Where("faid=?").Prepare(),
|
||||
exists: acc.Exists(fa, "faid").Prepare(),
|
||||
count: acc.Count(fa).Prepare(),
|
||||
countInForum: acc.Count(fa).Where("fid=?").Prepare(),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) DailyTick() error {
|
||||
fas, e := s.GetAll()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
for _, fa := range fas {
|
||||
if e := fa.Run(); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) Get(id int) (*ForumAction, error) {
|
||||
fa := ForumAction{ID: id}
|
||||
var str string
|
||||
e := s.get.QueryRow(id).Scan(&fa.Forum, &fa.RunOnTopicCreation, &fa.RunDaysAfterTopicCreation, &fa.RunDaysAfterTopicLastReply, &str, &fa.Extra)
|
||||
fa.Action = ConvStringToAct(str)
|
||||
return &fa, e
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) GetInForum(fid int) (fas []*ForumAction, e error) {
|
||||
rows, e := s.getInForum.Query(fid)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
var str string
|
||||
for rows.Next() {
|
||||
fa := ForumAction{Forum: fid}
|
||||
if e := rows.Scan(&fa.ID, &fa.RunOnTopicCreation, &fa.RunDaysAfterTopicCreation, &fa.RunDaysAfterTopicLastReply, &str, &fa.Extra); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
fa.Action = ConvStringToAct(str)
|
||||
fas = append(fas, &fa)
|
||||
}
|
||||
return fas, rows.Err()
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) GetAll() (fas []*ForumAction, e error) {
|
||||
rows, e := s.getAll.Query()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
var str string
|
||||
for rows.Next() {
|
||||
fa := ForumAction{}
|
||||
if e := rows.Scan(&fa.ID, &fa.Forum, &fa.RunOnTopicCreation, &fa.RunDaysAfterTopicCreation, &fa.RunDaysAfterTopicLastReply, &str, &fa.Extra); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
fa.Action = ConvStringToAct(str)
|
||||
fas = append(fas, &fa)
|
||||
}
|
||||
return fas, rows.Err()
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) GetNewTopicActions(fid int) (fas []*ForumAction, e error) {
|
||||
rows, e := s.getNewTopicActions.Query(fid)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
var str string
|
||||
for rows.Next() {
|
||||
fa := ForumAction{RunOnTopicCreation: true}
|
||||
if e := rows.Scan(&fa.ID, &fa.Forum, &fa.RunDaysAfterTopicCreation, &fa.RunDaysAfterTopicLastReply, &str, &fa.Extra); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
fa.Action = ConvStringToAct(str)
|
||||
fas = append(fas, &fa)
|
||||
}
|
||||
return fas, rows.Err()
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) Add(fa *ForumAction) (int, error) {
|
||||
res, e := s.add.Exec(fa.Forum, fa.RunOnTopicCreation, fa.RunDaysAfterTopicCreation, fa.RunDaysAfterTopicLastReply, ConvActToString(fa.Action), fa.Extra)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
lastID, e := res.LastInsertId()
|
||||
return int(lastID), e
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) Delete(id int) error {
|
||||
_, e := s.delete.Exec(id)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) Exists(id int) bool {
|
||||
err := s.exists.QueryRow(id).Scan(&id)
|
||||
if err != nil && err != ErrNoRows {
|
||||
LogError(err)
|
||||
}
|
||||
return err != ErrNoRows
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) Count() (count int) {
|
||||
err := s.count.QueryRow().Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionStore) CountInForum(fid int) (count int) {
|
||||
return Countf(s.countInForum, fid)
|
||||
}
|
||||
|
||||
/*type ForumActionRunnable struct {
|
||||
ID int
|
||||
ActionID int
|
||||
TargetID int
|
||||
TargetType int // 0 = topic
|
||||
RunAfter int //unixtime
|
||||
}
|
||||
|
||||
type ForumActionRunnableStoreInt interface {
|
||||
GetAfterTime(unix int) ([]*ForumActionRunnable, error)
|
||||
GetInForum(fid int) ([]*ForumActionRunnable, error)
|
||||
Delete(faid int) error
|
||||
DeleteInForum(fid int) error
|
||||
DeleteByActionID(faid int) error
|
||||
Count() int
|
||||
CountInForum(fid int) int
|
||||
}
|
||||
|
||||
type DefaultForumActionRunnableStore struct {
|
||||
delete *sql.Stmt
|
||||
deleteInForum *sql.Stmt
|
||||
count *sql.Stmt
|
||||
countInForum *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultForumActionRunnableStore(acc *qgen.Accumulator) (*DefaultForumActionRunnableStore, error) {
|
||||
fa := "forums_actions"
|
||||
return &DefaultForumActionRunnableStore{
|
||||
delete: acc.Delete(fa).Where("faid=?").Prepare(),
|
||||
deleteInForum: acc.Delete(fa).Where("fid=?").Prepare(),
|
||||
count: acc.Count(fa).Prepare(),
|
||||
countInForum: acc.Count(fa).Where("faid=?").Prepare(),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionRunnableStore) Delete(id int) error {
|
||||
_, e := s.delete.Exec(id)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionRunnableStore) DeleteInForum(fid int) error {
|
||||
_, e := s.deleteInForum.Exec(id)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionRunnableStore) Count() (count int) {
|
||||
err := s.count.QueryRow().Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (s *DefaultForumActionRunnableStore) CountInForum(fid int) (count int) {
|
||||
return Countf(s.countInForum, fid)
|
||||
}
|
||||
*/
|
@ -52,9 +52,9 @@ func (s *MemoryForumPermsStore) Init() error {
|
||||
// TODO: Optimise this?
|
||||
func (s *MemoryForumPermsStore) ReloadAll() error {
|
||||
DebugLog("Reloading the forum perms")
|
||||
fids, err := Forums.GetAllIDs()
|
||||
if err != nil {
|
||||
return err
|
||||
fids, e := Forums.GetAllIDs()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
for _, fid := range fids {
|
||||
if e := s.reload(fid); e != nil {
|
||||
@ -68,13 +68,13 @@ func (s *MemoryForumPermsStore) ReloadAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryForumPermsStore) parseForumPerm(perms []byte) (pperms *ForumPerms, err error) {
|
||||
func (s *MemoryForumPermsStore) parseForumPerm(perms []byte) (pperms *ForumPerms, e error) {
|
||||
DebugDetail("perms: ", string(perms))
|
||||
pperms = BlankForumPerms()
|
||||
err = json.Unmarshal(perms, &pperms)
|
||||
e = json.Unmarshal(perms, &pperms)
|
||||
pperms.ExtData = make(map[string]bool)
|
||||
pperms.Overrides = true
|
||||
return pperms, err
|
||||
return pperms, e
|
||||
}
|
||||
|
||||
func (s *MemoryForumPermsStore) Reload(fid int) error {
|
||||
@ -246,10 +246,10 @@ func (s *MemoryForumPermsStore) Get(fid, gid int) (fp *ForumPerms, err error) {
|
||||
|
||||
// TODO: Check if the forum exists?
|
||||
// TODO: Fix the races
|
||||
func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, err error) {
|
||||
fPermsPtr, err := s.Get(fid, gid)
|
||||
if err != nil {
|
||||
return fp, err
|
||||
func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, e error) {
|
||||
fPermsPtr, e := s.Get(fid, gid)
|
||||
if e != nil {
|
||||
return fp, e
|
||||
}
|
||||
return *fPermsPtr, nil
|
||||
}
|
||||
|
@ -81,18 +81,21 @@ type MemoryForumStore struct {
|
||||
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
||||
acc := qgen.NewAcc()
|
||||
f := "forums"
|
||||
set := func(s string) *sql.Stmt {
|
||||
return acc.Update(f).Set(s).Where("fid=?").Prepare()
|
||||
}
|
||||
// TODO: Do a proper delete
|
||||
return &MemoryForumStore{
|
||||
get: acc.Select(f).Columns("name, desc, tmpl, active, order, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Where("fid=?").Prepare(),
|
||||
getAll: acc.Select(f).Columns("fid, name, desc, tmpl, active, order, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Orderby("order ASC, fid ASC").Prepare(),
|
||||
delete: acc.Update(f).Set("name='',active=0").Where("fid=?").Prepare(),
|
||||
create: acc.Insert(f).Columns("name, desc, tmpl, active, preset").Fields("?,?,'',?,?").Prepare(),
|
||||
delete: set("name='',active=0"),
|
||||
create: acc.Insert(f).Columns("name,desc,tmpl,active,preset").Fields("?,?,'',?,?").Prepare(),
|
||||
count: acc.Count(f).Where("name != ''").Prepare(),
|
||||
updateCache: acc.Update(f).Set("lastTopicID=?, lastReplyerID=?").Where("fid=?").Prepare(),
|
||||
addTopics: acc.Update(f).Set("topicCount=topicCount+?").Where("fid=?").Prepare(),
|
||||
removeTopics: acc.Update(f).Set("topicCount=topicCount-?").Where("fid=?").Prepare(),
|
||||
lastTopic: acc.Select("topics").Columns("tid").Where("parentID=?").Orderby("lastReplyAt DESC, createdAt DESC").Limit("1").Prepare(),
|
||||
updateOrder: acc.Update(f).Set("order=?").Where("fid=?").Prepare(),
|
||||
updateCache: set("lastTopicID=?,lastReplyerID=?"),
|
||||
addTopics: set("topicCount=topicCount+?"),
|
||||
removeTopics: set("topicCount=topicCount-?"),
|
||||
lastTopic: acc.Select("topics").Columns("tid").Where("parentID=?").Orderby("lastReplyAt DESC,createdAt DESC").Limit("1").Prepare(),
|
||||
updateOrder: set("order=?"),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
|
@ -44,11 +44,13 @@ var groupStmts GroupStmts
|
||||
|
||||
func init() {
|
||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
ug := "users_groups"
|
||||
set := func(s string) *sql.Stmt {
|
||||
return acc.Update("users_groups").Set(s).Where("gid=?").Prepare()
|
||||
}
|
||||
groupStmts = GroupStmts{
|
||||
updateGroup: acc.Update(ug).Set("name=?,tag=?").Where("gid=?").Prepare(),
|
||||
updateGroupRank: acc.Update(ug).Set("is_admin=?,is_mod=?,is_banned=?").Where("gid=?").Prepare(),
|
||||
updateGroupPerms: acc.Update(ug).Set("permissions=?").Where("gid=?").Prepare(),
|
||||
updateGroup: set("name=?,tag=?"),
|
||||
updateGroupRank: set("is_admin=?,is_mod=?,is_banned=?"),
|
||||
updateGroupPerms: set("permissions=?"),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
|
@ -141,22 +141,20 @@ func (s *MemoryGroupStore) GetCopy(id int) (Group, error) {
|
||||
|
||||
func (s *MemoryGroupStore) Reload(id int) error {
|
||||
// TODO: Reload this data too
|
||||
g, err := s.Get(id)
|
||||
if err != nil {
|
||||
g, e := s.Get(id)
|
||||
if e != nil {
|
||||
LogError(errors.New("can't get cansee data for group #" + strconv.Itoa(id)))
|
||||
return nil
|
||||
}
|
||||
canSee := g.CanSee
|
||||
|
||||
g = &Group{ID: id, CanSee: canSee}
|
||||
err = s.get.QueryRow(id).Scan(&g.Name, &g.PermissionsText, &g.PluginPermsText, &g.IsMod, &g.IsAdmin, &g.IsBanned, &g.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
e = s.get.QueryRow(id).Scan(&g.Name, &g.PermissionsText, &g.PluginPermsText, &g.IsMod, &g.IsAdmin, &g.IsBanned, &g.Tag)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
err = s.initGroup(g)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
if e = s.initGroup(g); e != nil {
|
||||
LogError(e)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -166,19 +164,19 @@ func (s *MemoryGroupStore) Reload(id int) error {
|
||||
}
|
||||
|
||||
func (s *MemoryGroupStore) initGroup(g *Group) error {
|
||||
err := json.Unmarshal(g.PermissionsText, &g.Perms)
|
||||
if err != nil {
|
||||
log.Printf("group: %+v\n", g)
|
||||
e := json.Unmarshal(g.PermissionsText, &g.Perms)
|
||||
if e != nil {
|
||||
log.Printf("g: %+v\n", g)
|
||||
log.Print("bad group perms: ", g.PermissionsText)
|
||||
return err
|
||||
return e
|
||||
}
|
||||
DebugLogf(g.Name+": %+v\n", g.Perms)
|
||||
|
||||
err = json.Unmarshal(g.PluginPermsText, &g.PluginPerms)
|
||||
if err != nil {
|
||||
log.Printf("group: %+v\n", g)
|
||||
e = json.Unmarshal(g.PluginPermsText, &g.PluginPerms)
|
||||
if e != nil {
|
||||
log.Printf("g: %+v\n", g)
|
||||
log.Print("bad group plugin perms: ", g.PluginPermsText)
|
||||
return err
|
||||
return e
|
||||
}
|
||||
DebugLogf(g.Name+": %+v\n", g.PluginPerms)
|
||||
|
||||
@ -188,9 +186,9 @@ func (s *MemoryGroupStore) initGroup(g *Group) error {
|
||||
g.IsBanned = false
|
||||
}
|
||||
|
||||
err = s.userCount.QueryRow(g.ID).Scan(&g.UserCount)
|
||||
if err != sql.ErrNoRows {
|
||||
return err
|
||||
e = s.userCount.QueryRow(g.ID).Scan(&g.UserCount)
|
||||
if e != sql.ErrNoRows {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -649,12 +649,18 @@ type GroupForumPermPreset struct {
|
||||
|
||||
type PanelEditForumPage struct {
|
||||
*BasePanelPage
|
||||
ID int
|
||||
Name string
|
||||
Desc string
|
||||
Active bool
|
||||
Preset string
|
||||
Groups []GroupForumPermPreset
|
||||
ID int
|
||||
Name string
|
||||
Desc string
|
||||
Active bool
|
||||
Preset string
|
||||
Groups []GroupForumPermPreset
|
||||
Actions []*ForumActionAction
|
||||
}
|
||||
|
||||
type ForumActionAction struct {
|
||||
*ForumAction
|
||||
ActionName string
|
||||
}
|
||||
|
||||
type NameLangToggle struct {
|
||||
@ -793,11 +799,12 @@ type PanelDebugPage struct {
|
||||
DBVersion string
|
||||
Uptime string
|
||||
|
||||
OpenConns int
|
||||
DBConns int
|
||||
DBAdapter string
|
||||
|
||||
Goroutines int
|
||||
CPUs int
|
||||
HttpConns int
|
||||
|
||||
Tasks DebugPageTasks
|
||||
MemStats runtime.MemStats
|
||||
|
@ -39,11 +39,11 @@ func NewDefaultBlockStore(acc *qgen.Accumulator) (*DefaultBlockStore, error) {
|
||||
}
|
||||
|
||||
func (s *DefaultBlockStore) IsBlockedBy(blocker, blockee int) (bool, error) {
|
||||
err := s.isBlocked.QueryRow(blocker, blockee).Scan(&blocker)
|
||||
if err == ErrNoRows {
|
||||
e := s.isBlocked.QueryRow(blocker, blockee).Scan(&blocker)
|
||||
if e == ErrNoRows {
|
||||
return false, nil
|
||||
}
|
||||
return err == nil, err
|
||||
return e == nil, e
|
||||
}
|
||||
|
||||
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||
@ -55,34 +55,33 @@ func (s *DefaultBlockStore) BulkIsBlockedBy(blockers []int, blockee int) (bool,
|
||||
return s.IsBlockedBy(blockers[0], blockee)
|
||||
}
|
||||
idList, q := inqbuild(blockers)
|
||||
count, err := qgen.NewAcc().Count("users_blocks").Where("blocker IN(" + q + ") AND blockedUser=?").TotalP(idList...)
|
||||
if err == ErrNoRows {
|
||||
count, e := qgen.NewAcc().Count("users_blocks").Where("blocker IN(" + q + ") AND blockedUser=?").TotalP(idList...)
|
||||
if e == ErrNoRows {
|
||||
return false, nil
|
||||
}
|
||||
return count == 0, err
|
||||
return count == 0, e
|
||||
}
|
||||
|
||||
func (s *DefaultBlockStore) Add(blocker, blockee int) error {
|
||||
_, err := s.add.Exec(blocker, blockee)
|
||||
return err
|
||||
_, e := s.add.Exec(blocker, blockee)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultBlockStore) Remove(blocker, blockee int) error {
|
||||
_, err := s.remove.Exec(blocker, blockee)
|
||||
return err
|
||||
_, e := s.remove.Exec(blocker, blockee)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultBlockStore) BlockedByOffset(blocker, offset, perPage int) (uids []int, err error) {
|
||||
rows, err := s.blockedBy.Query(blocker, offset, perPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rows, e := s.blockedBy.Query(blocker, offset, perPage)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var uid int
|
||||
err := rows.Scan(&uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if e := rows.Scan(&uid); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
@ -90,9 +89,9 @@ func (s *DefaultBlockStore) BlockedByOffset(blocker, offset, perPage int) (uids
|
||||
}
|
||||
|
||||
func (s *DefaultBlockStore) BlockedByCount(blocker int) (count int) {
|
||||
err := s.blockedByCount.QueryRow(blocker).Scan(&count)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
e := s.blockedByCount.QueryRow(blocker).Scan(&count)
|
||||
if e != nil {
|
||||
LogError(e)
|
||||
}
|
||||
return count
|
||||
}
|
||||
@ -127,13 +126,13 @@ func NewDefaultFriendStore(acc *qgen.Accumulator) (*DefaultFriendStore, error) {
|
||||
}
|
||||
|
||||
func (s *DefaultFriendStore) AddInvite(requester, target int) error {
|
||||
_, err := s.addInvite.Exec(requester, target)
|
||||
return err
|
||||
_, e := s.addInvite.Exec(requester, target)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultFriendStore) Confirm(requester, target int) error {
|
||||
_, err := s.confirm.Exec(requester, target)
|
||||
return err
|
||||
_, e := s.confirm.Exec(requester, target)
|
||||
return e
|
||||
}
|
||||
|
||||
func (s *DefaultFriendStore) GetOwnSentInvites(uid int) ([]FriendInvite, error) {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/Azareal/Gosora/query_gen"
|
||||
qgen "github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
||||
type TaskStmts struct {
|
||||
@ -68,18 +68,22 @@ func AddShutdownTask(task func() error) {
|
||||
func ScheduledHalfSecondTaskCount() int {
|
||||
return len(ScheduledHalfSecondTasks)
|
||||
}
|
||||
|
||||
// ScheduledSecondTaskCount is not concurrency safe
|
||||
func ScheduledSecondTaskCount() int {
|
||||
return len(ScheduledSecondTasks)
|
||||
}
|
||||
|
||||
// ScheduledFifteenMinuteTaskCount is not concurrency safe
|
||||
func ScheduledFifteenMinuteTaskCount() int {
|
||||
return len(ScheduledFifteenMinuteTasks)
|
||||
}
|
||||
|
||||
// ScheduledHourTaskCount is not concurrency safe
|
||||
func ScheduledHourTaskCount() int {
|
||||
return len(ScheduledHourTasks)
|
||||
}
|
||||
|
||||
// ShutdownTaskCount is not concurrency safe
|
||||
func ShutdownTaskCount() int {
|
||||
return len(ShutdownTasks)
|
||||
@ -87,25 +91,23 @@ func ShutdownTaskCount() int {
|
||||
|
||||
// TODO: Use AddScheduledSecondTask
|
||||
func HandleExpiredScheduledGroups() error {
|
||||
rows, err := taskStmts.getExpiredScheduledGroups.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
rows, e := taskStmts.getExpiredScheduledGroups.Query()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var uid int
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&uid)
|
||||
if err != nil {
|
||||
return err
|
||||
if e := rows.Scan(&uid); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// Sneaky way of initialising a *User, please use the methods on the UserStore instead
|
||||
user := BlankUser()
|
||||
user.ID = uid
|
||||
err = user.RevertGroupUpdate()
|
||||
if err != nil {
|
||||
return err
|
||||
e = user.RevertGroupUpdate()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return rows.Err()
|
||||
@ -122,28 +124,28 @@ func HandleServerSync() error {
|
||||
}
|
||||
|
||||
var lastUpdate time.Time
|
||||
err := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
e := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
if lastUpdate.After(lastSync) {
|
||||
err = Forums.LoadForums()
|
||||
if err != nil {
|
||||
e = Forums.LoadForums()
|
||||
if e != nil {
|
||||
log.Print("Unable to reload the forums")
|
||||
return err
|
||||
return e
|
||||
}
|
||||
// TODO: Resync the groups
|
||||
// TODO: Resync the permissions
|
||||
err = LoadSettings()
|
||||
if err != nil {
|
||||
e = LoadSettings()
|
||||
if e != nil {
|
||||
log.Print("Unable to reload the settings")
|
||||
return err
|
||||
return e
|
||||
}
|
||||
err = WordFilters.ReloadAll()
|
||||
if err != nil {
|
||||
e = WordFilters.ReloadAll()
|
||||
if e != nil {
|
||||
log.Print("Unable to reload the word filters")
|
||||
return err
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -385,7 +385,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
|
||||
debugCache := DebugPageCache{1, 1, 1, 2, 2, 2, true}
|
||||
debugDatabase := DebugPageDatabase{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||
debugDisk := DebugPageDisk{1, 1, 1, 1, 1, 1}
|
||||
dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, debugTasks, memStats, debugCache, debugDatabase, debugDisk}
|
||||
dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, 1, debugTasks, memStats, debugCache, debugDatabase, debugDisk}
|
||||
t.AddStd("panel_debug", "c.PanelDebugPage", dpage)
|
||||
//t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter})
|
||||
|
||||
@ -395,9 +395,9 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
|
||||
if content == "" {
|
||||
return //log.Fatal("No content body for " + name)
|
||||
}
|
||||
err := writeFile("./tmpl_"+name+".go", content)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
e := writeFile("./tmpl_"+name+".go", content)
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
}
|
||||
wg.Add(1)
|
||||
@ -595,9 +595,9 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
|
||||
if tname != "" {
|
||||
tname = "_" + tname
|
||||
}
|
||||
err := writeFile(dirPrefix+"tmpl_"+name+tname+".jgo", content)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
e := writeFile(dirPrefix+"tmpl_"+name+tname+".jgo", content)
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
@ -737,9 +737,9 @@ func writeTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string)
|
||||
log.Print("Writing template list")
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
err := writeFile(prefix+"tmpl_list.go", getTemplateList(c, wg, prefix))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
e := writeFile(prefix+"tmpl_list.go", getTemplateList(c, wg, prefix))
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
288
common/topic.go
288
common/topic.go
@ -199,6 +199,7 @@ type TopicStmts struct {
|
||||
deleteActivity *sql.Stmt
|
||||
edit *sql.Stmt
|
||||
setPoll *sql.Stmt
|
||||
testSetCreatedAt *sql.Stmt
|
||||
createAction *sql.Stmt
|
||||
|
||||
getTopicUser *sql.Stmt // TODO: Can we get rid of this?
|
||||
@ -209,31 +210,35 @@ var topicStmts TopicStmts
|
||||
|
||||
func init() {
|
||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
t := "topics"
|
||||
t, w := "topics", "tid=?"
|
||||
set := func(s string) *sql.Stmt {
|
||||
return acc.Update(t).Set(s).Where(w).Prepare()
|
||||
}
|
||||
topicStmts = TopicStmts{
|
||||
getRids: acc.Select("replies").Columns("rid").Where("tid=?").Orderby("rid ASC").Limit("?,?").Prepare(),
|
||||
getRids: acc.Select("replies").Columns("rid").Where(w).Orderby("rid ASC").Limit("?,?").Prepare(),
|
||||
getReplies: acc.SimpleLeftJoin("replies AS r", "users AS u", "r.rid, r.content, r.createdBy, r.createdAt, r.lastEdit, r.lastEditBy, u.avatar, u.name, u.group, u.level, r.ip, r.likeCount, r.attachCount, r.actionType", "r.createdBy=u.uid", "r.tid=?", "r.rid ASC", "?,?"),
|
||||
getReplies2: acc.SimpleLeftJoin("replies AS r", "users AS u", "r.rid, r.content, r.createdBy, r.createdAt, r.lastEdit, r.lastEditBy, u.avatar, u.name, u.group, u.level, r.likeCount, r.attachCount, r.actionType", "r.createdBy=u.uid", "r.tid=?", "r.rid ASC", "?,?"),
|
||||
getReplies3: acc.Select("replies").Columns("rid, content, createdBy, createdAt, lastEdit, lastEditBy, likeCount, attachCount, actionType").Where("tid=?").Orderby("rid ASC").Limit("?,?").Prepare(),
|
||||
addReplies: acc.Update(t).Set("postCount=postCount+?, lastReplyBy=?, lastReplyAt=UTC_TIMESTAMP()").Where("tid=?").Prepare(),
|
||||
updateLastReply: acc.Update(t).Set("lastReplyID=?").Where("lastReplyID < ? AND tid=?").Prepare(),
|
||||
lock: acc.Update(t).Set("is_closed=1").Where("tid=?").Prepare(),
|
||||
unlock: acc.Update(t).Set("is_closed=0").Where("tid=?").Prepare(),
|
||||
moveTo: acc.Update(t).Set("parentID=?").Where("tid=?").Prepare(),
|
||||
stick: acc.Update(t).Set("sticky=1").Where("tid=?").Prepare(),
|
||||
unstick: acc.Update(t).Set("sticky=0").Where("tid=?").Prepare(),
|
||||
getReplies3: acc.Select("replies").Columns("rid,content,createdBy,createdAt,lastEdit,lastEditBy,likeCount,attachCount,actionType").Where(w).Orderby("rid ASC").Limit("?,?").Prepare(),
|
||||
addReplies: set("postCount=postCount+?,lastReplyBy=?,lastReplyAt=UTC_TIMESTAMP()"),
|
||||
updateLastReply: acc.Update(t).Set("lastReplyID=?").Where("lastReplyID<? AND tid=?").Prepare(),
|
||||
lock: set("is_closed=1"),
|
||||
unlock: set("is_closed=0"),
|
||||
moveTo: set("parentID=?"),
|
||||
stick: set("sticky=1"),
|
||||
unstick: set("sticky=0"),
|
||||
hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy=? and targetItem=? and targetType='topics'").Prepare(),
|
||||
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy, createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||
addLikesToTopic: acc.Update(t).Set("likeCount=likeCount+?").Where("tid=?").Prepare(),
|
||||
delete: acc.Delete(t).Where("tid=?").Prepare(),
|
||||
deleteReplies: acc.Delete("replies").Where("tid=?").Prepare(),
|
||||
createLike: acc.Insert("likes").Columns("weight,targetItem,targetType,sentBy,createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||
addLikesToTopic: set("likeCount=likeCount+?"),
|
||||
delete: acc.Delete(t).Where(w).Prepare(),
|
||||
deleteReplies: acc.Delete("replies").Where(w).Prepare(),
|
||||
deleteLikesForTopic: acc.Delete("likes").Where("targetItem=? AND targetType='topics'").Prepare(),
|
||||
deleteActivity: acc.Delete("activity_stream").Where("elementID=? AND elementType='topic'").Prepare(),
|
||||
edit: acc.Update(t).Set("title=?,content=?,parsed_content=?").Where("tid=?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter?
|
||||
edit: set("title=?,content=?,parsed_content=?"), // TODO: Only run the content update bits on non-polls, does this matter?
|
||||
setPoll: acc.Update(t).Set("poll=?").Where("tid=? AND poll=0").Prepare(),
|
||||
createAction: acc.Insert("replies").Columns("tid, actionType, ip, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
||||
testSetCreatedAt: set("createdAt=?"),
|
||||
createAction: acc.Insert("replies").Columns("tid,actionType,ip,createdBy,createdAt,lastUpdated,content,parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
||||
|
||||
getTopicUser: acc.SimpleLeftJoin("topics AS t", "users AS u", "t.title, t.content, t.createdBy, t.createdAt, t.lastReplyAt, t.lastReplyBy, t.lastReplyID, t.is_closed, t.sticky, t.parentID, t.ip, t.views, t.postCount, t.likeCount, t.attachCount,t.poll, u.name, u.avatar, u.group, u.level", "t.createdBy=u.uid", "tid=?", "", ""),
|
||||
getTopicUser: acc.SimpleLeftJoin("topics AS t", "users AS u", "t.title, t.content, t.createdBy, t.createdAt, t.lastReplyAt, t.lastReplyBy, t.lastReplyID, t.is_closed, t.sticky, t.parentID, t.ip, t.views, t.postCount, t.likeCount, t.attachCount,t.poll, u.name, u.avatar, u.group, u.level", "t.createdBy=u.uid", w, "", ""),
|
||||
getByReplyID: acc.SimpleLeftJoin("replies AS r", "topics AS t", "t.tid, t.title, t.content, t.createdBy, t.createdAt, t.is_closed, t.sticky, t.parentID, t.ip, t.views, t.postCount, t.likeCount, t.poll, t.data", "r.tid=t.tid", "rid=?", "", ""),
|
||||
}
|
||||
return acc.FirstError()
|
||||
@ -250,52 +255,58 @@ func (t *Topic) cacheRemove() {
|
||||
}
|
||||
|
||||
// TODO: Write a test for this
|
||||
func (t *Topic) AddReply(rid, uid int) (err error) {
|
||||
_, err = topicStmts.addReplies.Exec(1, uid, t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
func (t *Topic) AddReply(rid, uid int) (e error) {
|
||||
_, e = topicStmts.addReplies.Exec(1, uid, t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
_, err = topicStmts.updateLastReply.Exec(rid, rid, t.ID)
|
||||
_, e = topicStmts.updateLastReply.Exec(rid, rid, t.ID)
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Topic) Lock() (err error) {
|
||||
_, err = topicStmts.lock.Exec(t.ID)
|
||||
func (t *Topic) Lock() (e error) {
|
||||
_, e = topicStmts.lock.Exec(t.ID)
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Topic) Unlock() (err error) {
|
||||
_, err = topicStmts.unlock.Exec(t.ID)
|
||||
func (t *Topic) Unlock() (e error) {
|
||||
_, e = topicStmts.unlock.Exec(t.ID)
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Topic) MoveTo(destForum int) (err error) {
|
||||
_, err = topicStmts.moveTo.Exec(destForum, t.ID)
|
||||
func (t *Topic) MoveTo(destForum int) (e error) {
|
||||
_, e = topicStmts.moveTo.Exec(destForum, t.ID)
|
||||
t.cacheRemove()
|
||||
if err != nil {
|
||||
return err
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = Attachments.MoveTo(destForum, t.ID, "topics")
|
||||
if err != nil {
|
||||
return err
|
||||
e = Attachments.MoveTo(destForum, t.ID, "topics")
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return Attachments.MoveToByExtra(destForum, "replies", strconv.Itoa(t.ID))
|
||||
}
|
||||
|
||||
// TODO: We might want more consistent terminology rather than using stick in some places and pin in others. If you don't understand the difference, there is none, they are one and the same.
|
||||
func (t *Topic) Stick() (err error) {
|
||||
_, err = topicStmts.stick.Exec(t.ID)
|
||||
func (t *Topic) TestSetCreatedAt(s time.Time) (e error) {
|
||||
_, e = topicStmts.testSetCreatedAt.Exec(s, t.ID)
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Topic) Unstick() (err error) {
|
||||
_, err = topicStmts.unstick.Exec(t.ID)
|
||||
// TODO: We might want more consistent terminology rather than using stick in some places and pin in others. If you don't understand the difference, there is none, they are one and the same.
|
||||
func (t *Topic) Stick() (e error) {
|
||||
_, e = topicStmts.stick.Exec(t.ID)
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Topic) Unstick() (e error) {
|
||||
_, e = topicStmts.unstick.Exec(t.ID)
|
||||
t.cacheRemove()
|
||||
return e
|
||||
}
|
||||
|
||||
// TODO: Test this
|
||||
@ -323,49 +334,46 @@ func (t *Topic) Like(score, uid int) (err error) {
|
||||
|
||||
// TODO: Use a transaction
|
||||
func (t *Topic) Unlike(uid int) error {
|
||||
err := Likes.Delete(t.ID, "topics")
|
||||
if err != nil {
|
||||
return err
|
||||
e := Likes.Delete(t.ID, "topics")
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
_, err = topicStmts.addLikesToTopic.Exec(-1, t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
_, e = topicStmts.addLikesToTopic.Exec(-1, t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
_, err = userStmts.decLiked.Exec(1, uid)
|
||||
_, e = userStmts.decLiked.Exec(1, uid)
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
func handleLikedTopicReplies(tid int) error {
|
||||
rows, err := userStmts.getLikedRepliesOfTopic.Query(tid)
|
||||
if err != nil {
|
||||
return err
|
||||
rows, e := userStmts.getLikedRepliesOfTopic.Query(tid)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var rid int
|
||||
err := rows.Scan(&rid)
|
||||
if err != nil {
|
||||
return err
|
||||
if e := rows.Scan(&rid); e != nil {
|
||||
return e
|
||||
}
|
||||
_, err = replyStmts.deleteLikesForReply.Exec(rid)
|
||||
if err != nil {
|
||||
return err
|
||||
_, e = replyStmts.deleteLikesForReply.Exec(rid)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = Activity.DeleteByParams("like", rid, "post")
|
||||
if err != nil {
|
||||
return err
|
||||
e = Activity.DeleteByParams("like", rid, "post")
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func handleTopicAttachments(tid int) error {
|
||||
err := handleAttachments(userStmts.getAttachmentsOfTopic, tid)
|
||||
if err != nil {
|
||||
return err
|
||||
e := handleAttachments(userStmts.getAttachmentsOfTopic, tid)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return handleAttachments(userStmts.getAttachmentsOfTopic2, tid)
|
||||
}
|
||||
@ -375,120 +383,113 @@ func handleReplyAttachments(rid int) error {
|
||||
}
|
||||
|
||||
func handleAttachments(stmt *sql.Stmt, id int) error {
|
||||
rows, err := stmt.Query(id)
|
||||
if err != nil {
|
||||
return err
|
||||
rows, e := stmt.Query(id)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var aid int
|
||||
err := rows.Scan(&aid)
|
||||
if err != nil {
|
||||
return err
|
||||
if e := rows.Scan(&aid); e != nil {
|
||||
return e
|
||||
}
|
||||
a, err := Attachments.FGet(aid)
|
||||
if err != nil {
|
||||
return err
|
||||
a, e := Attachments.FGet(aid)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = deleteAttachment(a)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return err
|
||||
e = deleteAttachment(a)
|
||||
if e != nil && e != sql.ErrNoRows {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
// TODO: Only load a row per createdBy, maybe with group by?
|
||||
func handleTopicReplies(umap map[int]struct{}, uid, tid int) error {
|
||||
rows, err := userStmts.getRepliesOfTopic.Query(uid, tid)
|
||||
if err != nil {
|
||||
return err
|
||||
rows, e := userStmts.getRepliesOfTopic.Query(uid, tid)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var createdBy int
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&createdBy)
|
||||
if err != nil {
|
||||
return err
|
||||
if e := rows.Scan(&createdBy); e != nil {
|
||||
return e
|
||||
}
|
||||
umap[createdBy] = struct{}{}
|
||||
}
|
||||
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
// TODO: Use a transaction here
|
||||
func (t *Topic) Delete() error {
|
||||
/*creator, err := Users.Get(t.CreatedBy)
|
||||
if err == nil {
|
||||
err = creator.DecreasePostStats(WordCount(t.Content), true)
|
||||
if err != nil {
|
||||
return err
|
||||
/*creator, e := Users.Get(t.CreatedBy)
|
||||
if e == nil {
|
||||
e = creator.DecreasePostStats(WordCount(t.Content), true)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
} else if err != ErrNoRows {
|
||||
return err
|
||||
} else if e != ErrNoRows {
|
||||
return e
|
||||
}*/
|
||||
|
||||
// TODO: Clear reply cache too
|
||||
_, err := topicStmts.delete.Exec(t.ID)
|
||||
_, e := topicStmts.delete.Exec(t.ID)
|
||||
t.cacheRemove()
|
||||
if err != nil {
|
||||
return err
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = Forums.RemoveTopic(t.ParentID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return err
|
||||
e = Forums.RemoveTopic(t.ParentID)
|
||||
if e != nil && e != ErrNoRows {
|
||||
return e
|
||||
}
|
||||
_, err = topicStmts.deleteLikesForTopic.Exec(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
_, e = topicStmts.deleteLikesForTopic.Exec(t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
if t.PostCount > 1 {
|
||||
err = handleLikedTopicReplies(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
if e = handleLikedTopicReplies(t.ID); e != nil {
|
||||
return e
|
||||
}
|
||||
umap := make(map[int]struct{})
|
||||
err = handleTopicReplies(umap, t.CreatedBy, t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
e = handleTopicReplies(umap, t.CreatedBy, t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
_, err = topicStmts.deleteReplies.Exec(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
_, e = topicStmts.deleteReplies.Exec(t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
for uid := range umap {
|
||||
err = (&User{ID: uid}).RecalcPostStats()
|
||||
if err != nil {
|
||||
//log.Printf("err: %+v\n", err)
|
||||
return err
|
||||
e = (&User{ID: uid}).RecalcPostStats()
|
||||
if e != nil {
|
||||
//log.Printf("e: %+v\n", e)
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
err = (&User{ID: t.CreatedBy}).RecalcPostStats()
|
||||
if err != nil {
|
||||
return err
|
||||
e = (&User{ID: t.CreatedBy}).RecalcPostStats()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = handleTopicAttachments(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
e = handleTopicAttachments(t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = Subscriptions.DeleteResource(t.ID, "topic")
|
||||
if err != nil {
|
||||
return err
|
||||
e = Subscriptions.DeleteResource(t.ID, "topic")
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
_, err = topicStmts.deleteActivity.Exec(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
_, e = topicStmts.deleteActivity.Exec(t.ID)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
if t.Poll > 0 {
|
||||
err = (&Poll{ID: t.Poll}).Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
e = (&Poll{ID: t.Poll}).Delete()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -513,9 +514,9 @@ func (t *Topic) Update(name, content string) error {
|
||||
}
|
||||
|
||||
func (t *Topic) SetPoll(pollID int) error {
|
||||
_, err := topicStmts.setPoll.Exec(pollID, t.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll
|
||||
_, e := topicStmts.setPoll.Exec(pollID, t.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll
|
||||
t.cacheRemove()
|
||||
return err
|
||||
return e
|
||||
}
|
||||
|
||||
// TODO: Have this go through the ReplyStore?
|
||||
@ -543,22 +544,19 @@ func (t *Topic) CreateActionReply(action, ip string, uid int) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func GetRidsForTopic(tid, offset int) (rids []int, err error) {
|
||||
rows, err := topicStmts.getRids.Query(tid, offset, Config.ItemsPerPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func GetRidsForTopic(tid, offset int) (rids []int, e error) {
|
||||
rows, e := topicStmts.getRids.Query(tid, offset, Config.ItemsPerPage)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var rid int
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&rid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if e := rows.Scan(&rid); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
rids = append(rids, rid)
|
||||
}
|
||||
|
||||
return rids, rows.Err()
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@ type weakpassHolder struct {
|
||||
|
||||
func InitWeakPasswords() error {
|
||||
var weakpass weakpassHolder
|
||||
err := unmarshalJsonFile("./config/weakpass_default.json", &weakpass)
|
||||
if err != nil {
|
||||
return err
|
||||
e := unmarshalJsonFile("./config/weakpass_default.json", &weakpass)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
wcon := make(map[string]struct{})
|
||||
@ -41,9 +41,9 @@ func InitWeakPasswords() error {
|
||||
}
|
||||
|
||||
weakpass = weakpassHolder{}
|
||||
err = unmarshalJsonFileIgnore404("./config/weakpass.json", &weakpass)
|
||||
if err != nil {
|
||||
return err
|
||||
e = unmarshalJsonFileIgnore404("./config/weakpass.json", &weakpass)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
for _, item := range weakpass.Contains {
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
*
|
||||
* Gosora WebSocket Subsystem
|
||||
* Copyright Azareal 2017 - 2020
|
||||
* Copyright Azareal 2017 - 2021
|
||||
*
|
||||
*/
|
||||
package common
|
||||
@ -149,12 +149,12 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||
if wsUser.User.ID == 0 {
|
||||
return
|
||||
}
|
||||
_, tid, err := ParseSEOURL(page)
|
||||
if err != nil {
|
||||
_, tid, e := ParseSEOURL(page)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
topic, err := Topics.Get(tid)
|
||||
if err != nil {
|
||||
topic, e := Topics.Get(tid)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
if !Forums.Exists(topic.ParentID) {
|
||||
@ -169,10 +169,10 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||
return
|
||||
}*/
|
||||
|
||||
fperms, err := FPStore.Get(topic.ParentID, usercpy.Group)
|
||||
if err == ErrNoRows {
|
||||
fperms, e := FPStore.Get(topic.ParentID, usercpy.Group)
|
||||
if e == ErrNoRows {
|
||||
fperms = BlankForumPerms()
|
||||
} else if err != nil {
|
||||
} else if e != nil {
|
||||
return
|
||||
}
|
||||
cascadeForumPerms(fperms, usercpy)
|
||||
@ -202,9 +202,9 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||
default:
|
||||
return
|
||||
}
|
||||
err := wsUser.SetPageForSocket(conn, page)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
e := wsUser.SetPageForSocket(conn, page)
|
||||
if e != nil {
|
||||
LogError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ func wsPageResume(wsUser *WSUser, conn *websocket.Conn, page string, resume int6
|
||||
/*if resume >= hub.lastTick.Unix() {
|
||||
conn.Write([]byte("resume tooslow"))
|
||||
} else {
|
||||
conn.Write([]byte("resume success"))
|
||||
conn.Write([]byte("resume success"))
|
||||
}*/
|
||||
default:
|
||||
return
|
||||
@ -248,8 +248,8 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||
return
|
||||
}
|
||||
wsUser.FinalizePage(page, func() {
|
||||
_, tid, err := ParseSEOURL(page)
|
||||
if err != nil {
|
||||
_, tid, e := ParseSEOURL(page)
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
topicMutex.Lock()
|
||||
@ -258,8 +258,7 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
_, ok = topic[wsUser]
|
||||
if !ok {
|
||||
if _, ok = topic[wsUser]; !ok {
|
||||
return
|
||||
}
|
||||
delete(topic, wsUser)
|
||||
@ -272,9 +271,9 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||
delete(adminStatsWatchers, conn)
|
||||
adminStatsMutex.Unlock()
|
||||
}
|
||||
err := wsUser.SetPageForSocket(conn, "")
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
e := wsUser.SetPageForSocket(conn, "")
|
||||
if e != nil {
|
||||
LogError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,9 +289,7 @@ var adminStatsMutex sync.RWMutex
|
||||
func adminStatsTicker() {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
lastUonline := -1
|
||||
lastGonline := -1
|
||||
lastTotonline := -1
|
||||
lastUonline, lastGonline, lastTotonline := -1, -1, -1
|
||||
lastCPUPerc := -1
|
||||
var lastAvailableRAM int64 = -1
|
||||
var noStatUpdates, noRAMUpdates bool
|
||||
|
@ -26,11 +26,11 @@ func init() {
|
||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
w := "widgets"
|
||||
widgetStmts = WidgetStmts{
|
||||
//getList: acc.Select(w).Columns("wid, position, side, type, active, location, data").Orderby("position ASC").Prepare(),
|
||||
getDockList: acc.Select(w).Columns("wid, position, type, active, location, data").Where("side=?").Orderby("position ASC").Prepare(),
|
||||
//getList: acc.Select(w).Columns("wid,position,side,type,active,location,data").Orderby("position ASC").Prepare(),
|
||||
getDockList: acc.Select(w).Columns("wid,position,type,active,location,data").Where("side=?").Orderby("position ASC").Prepare(),
|
||||
//model: acc.SimpleModel(w,"position,type,active,location,data","wid"),
|
||||
delete: acc.Delete(w).Where("wid=?").Prepare(),
|
||||
create: acc.Insert(w).Columns("position, side, type, active, location, data").Fields("?,?,?,?,?,?").Prepare(),
|
||||
create: acc.Insert(w).Columns("position,side,type,active,location,data").Fields("?,?,?,?,?,?").Prepare(),
|
||||
update: acc.Update(w).Set("position=?,side=?,type=?,active=?,location=?,data=?").Where("wid=?").Prepare(),
|
||||
}
|
||||
return acc.FirstError()
|
||||
@ -73,10 +73,10 @@ func (w *Widget) Delete() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Widget) Copy() (owidget *Widget) {
|
||||
owidget = &Widget{}
|
||||
*owidget = *w
|
||||
return owidget
|
||||
func (w *Widget) Copy() (ow *Widget) {
|
||||
ow = &Widget{}
|
||||
*ow = *w
|
||||
return ow
|
||||
}
|
||||
|
||||
// TODO: Test this
|
||||
|
@ -28,8 +28,8 @@ func (u *WSUser) Ping() error {
|
||||
continue
|
||||
}
|
||||
socket.conn.SetWriteDeadline(time.Now().Add(time.Minute))
|
||||
err := socket.conn.WriteMessage(websocket.PingMessage, nil)
|
||||
if err != nil {
|
||||
e := socket.conn.WriteMessage(websocket.PingMessage, nil)
|
||||
if e != nil {
|
||||
socket.conn.Close()
|
||||
}
|
||||
}
|
||||
@ -42,9 +42,9 @@ func (u *WSUser) WriteAll(msg string) error {
|
||||
if socket == nil {
|
||||
continue
|
||||
}
|
||||
w, err := socket.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
w, e := socket.conn.NextWriter(websocket.TextMessage)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
_, _ = w.Write(msgbytes)
|
||||
w.Close()
|
||||
@ -66,8 +66,8 @@ func (u *WSUser) WriteToPageBytes(msg []byte, page string) error {
|
||||
if socket.Page != page {
|
||||
continue
|
||||
}
|
||||
w, err := socket.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
w, e := socket.conn.NextWriter(websocket.TextMessage)
|
||||
if e != nil {
|
||||
continue // Skip dead sockets, a dedicated goroutine handles those
|
||||
}
|
||||
_, _ = w.Write(msg)
|
||||
@ -90,8 +90,8 @@ func (u *WSUser) WriteToPageBytesMulti(msgs [][]byte, page string) error {
|
||||
if socket.Page != page {
|
||||
continue
|
||||
}
|
||||
w, err := socket.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
w, e := socket.conn.NextWriter(websocket.TextMessage)
|
||||
if e != nil {
|
||||
continue // Skip dead sockets, a dedicated goroutine handles those
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
@ -184,7 +184,7 @@ func (u *WSUser) InPage(page string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *WSUser) FinalizePage(page string, handle func()) {
|
||||
func (u *WSUser) FinalizePage(page string, h func()) {
|
||||
u.Lock()
|
||||
defer u.Unlock()
|
||||
for _, socket := range u.Sockets {
|
||||
@ -195,5 +195,5 @@ func (u *WSUser) FinalizePage(page string, handle func()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
handle()
|
||||
h()
|
||||
}
|
||||
|
922
gen_router.go
922
gen_router.go
File diff suppressed because it is too large
Load Diff
@ -31,18 +31,18 @@ func Lookup(name string) (InstallAdapter, bool) {
|
||||
|
||||
func createAdmin() error {
|
||||
fmt.Println("Creating the admin user")
|
||||
hashedPassword, salt, err := BcryptGeneratePassword("password")
|
||||
if err != nil {
|
||||
return err
|
||||
hashedPassword, salt, e := BcryptGeneratePassword("password")
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// Build the admin user query
|
||||
adminUserStmt, err := qgen.Builder.SimpleInsert("users", "name, password, salt, email, group, is_super_admin, active, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt, message, last_ip", "'Admin',?,?,'admin@localhost',1,1,1,UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''")
|
||||
if err != nil {
|
||||
return err
|
||||
adminUserStmt, e := qgen.Builder.SimpleInsert("users", "name, password, salt, email, group, is_super_admin, active, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt, message, last_ip", "'Admin',?,?,'admin@localhost',1,1,1,UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''")
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// Run the admin user query
|
||||
_, err = adminUserStmt.Exec(hashedPassword, salt)
|
||||
return err
|
||||
_, e = adminUserStmt.Exec(hashedPassword, salt)
|
||||
return e
|
||||
}
|
||||
|
@ -56,7 +56,9 @@
|
||||
"megapost_min_words":"Mega Post Minimum Words",
|
||||
"meta_desc":"Meta Description",
|
||||
"rapid_loading":"Rapid Loaded?",
|
||||
"google_site_verify":"Google Site Verify"
|
||||
"google_site_verify":"Google Site Verify",
|
||||
"avatar_visibility":"Avatar Visibility",
|
||||
"avatar_visibility_label":"Everyone,Member Only"
|
||||
},
|
||||
|
||||
"PermPresets": {
|
||||
@ -479,14 +481,14 @@
|
||||
"panel_rank_guests":"Guests",
|
||||
"panel_rank_members":"Members",
|
||||
|
||||
"panel_preset_everyone":"Everyone",
|
||||
"panel_preset_announcements":"Announcements",
|
||||
"panel_preset_member_only":"Member Only",
|
||||
"panel_preset_staff_only":"Staff Only",
|
||||
"panel_preset_admin_only":"Admin Only",
|
||||
"panel_preset_archive":"Archive",
|
||||
"panel_preset_custom":"Custom",
|
||||
"panel_preset_public":"Public",
|
||||
"panel.preset_everyone":"Everyone",
|
||||
"panel.preset_announcements":"Announcements",
|
||||
"panel.preset_member_only":"Member Only",
|
||||
"panel.preset_staff_only":"Staff Only",
|
||||
"panel.preset_admin_only":"Admin Only",
|
||||
"panel.preset_archive":"Archive",
|
||||
"panel.preset_custom":"Custom",
|
||||
"panel.preset_public":"Public",
|
||||
"panel_active_hidden":"Hidden",
|
||||
|
||||
"panel_perms_no_access":"No Access",
|
||||
@ -693,7 +695,7 @@
|
||||
"forum_start_one":"Start one?",
|
||||
|
||||
"forums_head":"Forums",
|
||||
"forums_no_description":"No description",
|
||||
"forums_no_desc":"No description",
|
||||
"forums_none":"None",
|
||||
"forums_no_forums":"You don't have access to any forums.",
|
||||
|
||||
@ -944,26 +946,40 @@
|
||||
"panel.forums_create_head":"Add Forum",
|
||||
"panel.forums_create_name_label":"Name",
|
||||
"panel.forums_create_name":"Super Secret Forum",
|
||||
"panel.forums_create_description_label":"Description",
|
||||
"panel.forums_create_description":"Where all the super secret stuff happens",
|
||||
"panel.forums_create_desc_label":"Description",
|
||||
"panel.forums_create_desc":"Where all the super secret stuff happens",
|
||||
"panel.forums_active_label":"Active",
|
||||
"panel.forums_preset_label":"Preset",
|
||||
"panel.forums_create_button":"Add Forum",
|
||||
"panel.forums_update_order_button":"Update Order",
|
||||
"panel.forums_order_updated":"The forums have been successfully updated",
|
||||
|
||||
"panel_forum_head_suffix":" Forum",
|
||||
"panel_forum_name":"Name",
|
||||
"panel_forum_name_placeholder":"General Forum",
|
||||
"panel_forum_description":"Description",
|
||||
"panel_forum_description_placeholder":"Where the general stuff happens",
|
||||
"panel_forum_active":"Active",
|
||||
"panel_forum_preset":"Preset",
|
||||
"panel_forum_update_button":"Update Forum",
|
||||
"panel_forum_permissions_head":"Forum Permissions",
|
||||
"panel_forum_edit_button":"Edit",
|
||||
"panel_forum_short_update_button":"Update",
|
||||
"panel_forum_full_edit_button":"Full Edit",
|
||||
"panel.forum_head_suffix":" Forum",
|
||||
"panel.forum_name":"Name",
|
||||
"panel.forum_name_placeholder":"General Forum",
|
||||
"panel.forum_desc":"Description",
|
||||
"panel.forum_desc_placeholder":"Where the general stuff happens",
|
||||
"panel.forum_active":"Active",
|
||||
"panel.forum_preset":"Preset",
|
||||
"panel.forum_update_button":"Update Forum",
|
||||
"panel.forum_permissions_head":"Forum Permissions",
|
||||
"panel.forum_edit_button":"Edit",
|
||||
"panel.forum_short_update_button":"Update",
|
||||
"panel.forum_full_edit_button":"Full Edit",
|
||||
|
||||
"panel.forum_actions_head":"Actions",
|
||||
"panel.forum_actions_create_head":"Create Action",
|
||||
"panel.forum_action_run_on_topic_creation":"Run on Topic Creation",
|
||||
"panel.forum_action_run_days_after_topic_creation":"Run Days After Topic Creation",
|
||||
"panel.forum_action_run_days_after_topic_last_reply":"Run Days After Topic Last Reply",
|
||||
"panel.forum_action_action":"Action",
|
||||
"panel.forum_action_action_delete":"Delete",
|
||||
"panel.forum_action_action_lock":"Lock",
|
||||
"panel.forum_action_action_unlock":"Unlock",
|
||||
"panel.forum_action_action_move":"Move",
|
||||
"panel.forum_action_extra":"Extra",
|
||||
"panel.forum_action_create_button":"Create Action",
|
||||
|
||||
"panel_forum_delete_are_you_sure":"Are you sure you want to delete the '%s' forum?",
|
||||
|
||||
"panel_groups_head":"Groups",
|
||||
@ -1244,6 +1260,7 @@
|
||||
|
||||
"panel_debug_goroutine_count_label":"Goroutines",
|
||||
"panel_debug_cpu_count_label":"CPUs",
|
||||
"panel_debug_http_conns_label":"HTTP Conns",
|
||||
|
||||
"panel_debug_tasks":"Tasks",
|
||||
"panel_debug_tasks_half_second":"Half Second",
|
||||
|
14
main.go
14
main.go
@ -143,10 +143,10 @@ func storeInit() (e error) {
|
||||
if e != nil {
|
||||
return ws(e)
|
||||
}
|
||||
/*c.ForumActionStore, e = c.NewDefaultForumActionStore(acc)
|
||||
c.ForumActionStore, e = c.NewDefaultForumActionStore(acc)
|
||||
if e != nil {
|
||||
return ws(e)
|
||||
}*/
|
||||
}
|
||||
c.Convos, e = c.NewDefaultConversationStore(acc)
|
||||
if e != nil {
|
||||
return ws(e)
|
||||
@ -549,8 +549,7 @@ func main() {
|
||||
|
||||
// Resource Management Goroutine
|
||||
go func() {
|
||||
uc := c.Users.GetCache()
|
||||
tc := c.Topics.GetCache()
|
||||
uc, tc := c.Users.GetCache(), c.Topics.GetCache()
|
||||
if uc == nil && tc == nil {
|
||||
return
|
||||
}
|
||||
@ -630,7 +629,7 @@ func main() {
|
||||
|
||||
func startServer() {
|
||||
// We might not need the timeouts, if we're behind a reverse-proxy like Nginx
|
||||
newServer := func(addr string, handler http.Handler) *http.Server {
|
||||
newServer := func(addr string, h http.Handler) *http.Server {
|
||||
rtime := c.Config.ReadTimeout
|
||||
if rtime == 0 {
|
||||
rtime = 8
|
||||
@ -650,8 +649,9 @@ func startServer() {
|
||||
itime = 0
|
||||
}
|
||||
return &http.Server{
|
||||
Addr: addr,
|
||||
Handler: handler,
|
||||
Addr: addr,
|
||||
Handler: h,
|
||||
ConnState: c.ConnWatch.StateChange,
|
||||
|
||||
ReadTimeout: time.Duration(rtime) * time.Second,
|
||||
WriteTimeout: time.Duration(wtime) * time.Second,
|
||||
|
116
misc_test.go
116
misc_test.go
@ -1128,8 +1128,8 @@ func TestReplyStore(t *testing.T) {
|
||||
|
||||
func testReplyStore(t *testing.T, newID int, ip string) {
|
||||
ex, exf := exp(t), expf(t)
|
||||
replyTest2 := func(r *c.Reply, err error, rid, parentID, createdBy int, content, ip string) {
|
||||
expectNilErr(t, err)
|
||||
replyTest2 := func(r *c.Reply, e error, rid, parentID, createdBy int, content, ip string) {
|
||||
expectNilErr(t, e)
|
||||
exf(r.ID == rid, "RID #%d has the wrong ID. It should be %d not %d", rid, rid, r.ID)
|
||||
exf(r.ParentID == parentID, "The parent topic of RID #%d should be %d not %d", rid, parentID, r.ParentID)
|
||||
exf(r.CreatedBy == createdBy, "The creator of RID #%d should be %d not %d", rid, createdBy, r.CreatedBy)
|
||||
@ -1138,12 +1138,12 @@ func testReplyStore(t *testing.T, newID int, ip string) {
|
||||
}
|
||||
|
||||
replyTest := func(rid, parentID, createdBy int, content, ip string) {
|
||||
r, err := c.Rstore.Get(rid)
|
||||
replyTest2(r, err, rid, parentID, createdBy, content, ip)
|
||||
r, err = c.Rstore.GetCache().Get(rid)
|
||||
replyTest2(r, err, rid, parentID, createdBy, content, ip)
|
||||
r, e := c.Rstore.Get(rid)
|
||||
replyTest2(r, e, rid, parentID, createdBy, content, ip)
|
||||
r, e = c.Rstore.GetCache().Get(rid)
|
||||
replyTest2(r, e, rid, parentID, createdBy, content, ip)
|
||||
}
|
||||
replyTest(1, 1, 1, "A reply!", "::1")
|
||||
replyTest(1, 1, 1, "A reply!", "")
|
||||
|
||||
// ! This is hard to do deterministically as the system may pre-load certain items but let's give it a try:
|
||||
//_, err = c.Rstore.GetCache().Get(1)
|
||||
@ -2279,6 +2279,108 @@ func TestWidgets(t *testing.T) {
|
||||
exf(len(widgets) == 0, "RightSidebar should have 0 items, not %d", len(widgets))
|
||||
}
|
||||
|
||||
/*type ForumActionStoreInt interface {
|
||||
Get(faid int) (*ForumAction, error)
|
||||
GetInForum(fid int) ([]*ForumAction, error)
|
||||
GetAll() ([]*ForumAction, error)
|
||||
GetNewTopicActions(fid int) ([]*ForumAction, error)
|
||||
|
||||
Add(fa *ForumAction) (int, error)
|
||||
Delete(faid int) error
|
||||
Exists(faid int) bool
|
||||
Count() int
|
||||
CountInForum(fid int) int
|
||||
|
||||
DailyTick() error
|
||||
}*/
|
||||
|
||||
func TestForumActions(t *testing.T) {
|
||||
ex, exf, s := exp(t), expf(t), c.ForumActionStore
|
||||
|
||||
count := s.CountInForum(-1)
|
||||
exf(count == 0, "count should be %d not %d", 0, count)
|
||||
count = s.CountInForum(0)
|
||||
exf(count == 0, "count in 0 should be %d not %d", 0, count)
|
||||
ex(!s.Exists(-1), "faid -1 should not exist")
|
||||
ex(!s.Exists(0), "faid 0 should not exist")
|
||||
_, e := s.Get(-1)
|
||||
recordMustNotExist(t, e, "faid -1 should not exist")
|
||||
_, e = s.Get(0)
|
||||
recordMustNotExist(t, e, "faid 0 should not exist")
|
||||
|
||||
noActions := func(fid, faid int) {
|
||||
/*sfid, */ sfaid := /*strconv.Itoa(fid), */ strconv.Itoa(faid)
|
||||
count := s.Count()
|
||||
exf(count == 0, "count should be %d not %d", 0, count)
|
||||
count = s.CountInForum(fid)
|
||||
exf(count == 0, "count in %d should be %d not %d", fid, 0, count)
|
||||
exf(!s.Exists(faid), "faid %d should not exist", faid)
|
||||
_, e = s.Get(faid)
|
||||
recordMustNotExist(t, e, "faid "+sfaid+" should not exist")
|
||||
fas, e := s.GetInForum(fid)
|
||||
//recordMustNotExist(t, e, "fid "+sfid+" should not have any actions")
|
||||
expectNilErr(t, e) // TODO: Why does this not return ErrNoRows?
|
||||
exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas))
|
||||
fas, e = s.GetAll()
|
||||
//recordMustNotExist(t, e, "there should not be any actions")
|
||||
expectNilErr(t, e) // TODO: Why does this not return ErrNoRows?
|
||||
exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas))
|
||||
fas, e = s.GetNewTopicActions(fid)
|
||||
//recordMustNotExist(t, e, "fid "+sfid+" should not have any new topic actions")
|
||||
expectNilErr(t, e) // TODO: Why does this not return ErrNoRows?
|
||||
exf(len(fas) == 0, "len(fas) should be %d not %d", 0, len(fas))
|
||||
}
|
||||
noActions(1, 1)
|
||||
|
||||
fid, e := c.Forums.Create("Forum Action Test", "Forum Action Test", true, "")
|
||||
expectNilErr(t, e)
|
||||
noActions(fid, 1)
|
||||
|
||||
faid, e := c.ForumActionStore.Add(&c.ForumAction{
|
||||
Forum: fid,
|
||||
RunOnTopicCreation: false,
|
||||
RunDaysAfterTopicCreation: 1,
|
||||
RunDaysAfterTopicLastReply: 0,
|
||||
Action: c.ForumActionLock,
|
||||
Extra: "",
|
||||
})
|
||||
expectNilErr(t, e)
|
||||
exf(faid == 1, "faid should be %d not %d", 1, faid)
|
||||
count = s.Count()
|
||||
exf(count == 1, "count should be %d not %d", 1, count)
|
||||
count = s.CountInForum(fid)
|
||||
exf(count == 1, "count in %d should be %d not %d", fid, 1, count)
|
||||
exf(s.Exists(faid), "faid %d should exist", faid)
|
||||
|
||||
fa, e := s.Get(faid)
|
||||
expectNilErr(t, e)
|
||||
exf(fa.ID == faid, "fa.ID should be %d not %d", faid, fa.ID)
|
||||
exf(fa.Forum == fid, "fa.Forum should be %d not %d", fid, fa.Forum)
|
||||
exf(fa.RunOnTopicCreation == false, "fa.RunOnTopicCreation should be false")
|
||||
exf(fa.RunDaysAfterTopicCreation == 1, "fa.RunDaysAfterTopicCreation should be %d not %d", 1, fa.RunDaysAfterTopicCreation)
|
||||
exf(fa.RunDaysAfterTopicLastReply == 0, "fa.RunDaysAfterTopicLastReply should be %d not %d", 0, fa.RunDaysAfterTopicLastReply)
|
||||
exf(fa.Action == c.ForumActionLock, "fa.Action should be %d not %d", c.ForumActionLock, fa.Action)
|
||||
exf(fa.Extra == "", "fa.Extra should be '%s' not '%s'", "", fa.Extra)
|
||||
|
||||
tid, e := c.Topics.Create(fid, "Forum Action Topic", "Forum Action Topic", 1, "")
|
||||
expectNilErr(t, e)
|
||||
topic, e := c.Topics.Get(tid)
|
||||
expectNilErr(t, e)
|
||||
dayAgo := time.Now().AddDate(0, 0, -5)
|
||||
expectNilErr(t, topic.TestSetCreatedAt(dayAgo))
|
||||
expectNilErr(t, fa.Run())
|
||||
topic, e = c.Topics.Get(tid)
|
||||
expectNilErr(t, e)
|
||||
ex(topic.IsClosed, "topic.IsClosed should be true")
|
||||
/*_, e = c.Rstore.Create(topic, "Forum Action Reply", "", 1)
|
||||
expectNilErr(t, e)*/
|
||||
|
||||
_ = tid
|
||||
|
||||
expectNilErr(t, s.Delete(faid))
|
||||
noActions(fid, faid)
|
||||
}
|
||||
|
||||
func TestTopicList(t *testing.T) {
|
||||
ex, exf := exp(t), expf(t)
|
||||
fid, err := c.Forums.Create("Test Forum", "Desc for test forum", true, "")
|
||||
|
@ -53,6 +53,7 @@ func init() {
|
||||
addPatch(33, patch33)
|
||||
addPatch(34, patch34)
|
||||
addPatch(35, patch35)
|
||||
addPatch(36, patch36)
|
||||
}
|
||||
|
||||
func bcol(col string, val bool) qgen.DBTableColumn {
|
||||
@ -168,18 +169,17 @@ func patch0(scanner *bufio.Scanner) (err error) {
|
||||
}
|
||||
|
||||
func patch1(scanner *bufio.Scanner) error {
|
||||
routes := map[string]string{
|
||||
return renameRoutes(map[string]string{
|
||||
"routeAccountEditCriticalSubmit": "routes.AccountEditCriticalSubmit",
|
||||
"routeAccountEditAvatar": "routes.AccountEditAvatar",
|
||||
"routeAccountEditAvatarSubmit": "routes.AccountEditAvatarSubmit",
|
||||
"routeAccountEditUsername": "routes.AccountEditUsername",
|
||||
"routeAccountEditUsernameSubmit": "routes.AccountEditUsernameSubmit",
|
||||
}
|
||||
return renameRoutes(routes)
|
||||
})
|
||||
}
|
||||
|
||||
func patch2(scanner *bufio.Scanner) error {
|
||||
routes := map[string]string{
|
||||
return renameRoutes(map[string]string{
|
||||
"routeLogout": "routes.AccountLogout",
|
||||
"routeShowAttachment": "routes.ShowAttachment",
|
||||
"routeChangeTheme": "routes.ChangeTheme",
|
||||
@ -189,8 +189,7 @@ func patch2(scanner *bufio.Scanner) error {
|
||||
"routeDynamic": "routes.DynamicRoute",
|
||||
"routeUploads": "routes.UploadedFile",
|
||||
"BadRoute": "routes.BadRoute",
|
||||
}
|
||||
return renameRoutes(routes)
|
||||
})
|
||||
}
|
||||
|
||||
func patch3(scanner *bufio.Scanner) error {
|
||||
@ -248,14 +247,14 @@ func patch4(scanner *bufio.Scanner) error {
|
||||
"routePanelForumsEditPermsAdvanceSubmit": "panel.ForumsEditPermsAdvanceSubmit",
|
||||
"routePanelBackups": "panel.Backups",
|
||||
}
|
||||
err := renameRoutes(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
e := renameRoutes(routes)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
err = execStmt(qgen.Builder.SimpleDelete("settings", "name='url_tags'"))
|
||||
if err != nil {
|
||||
return err
|
||||
e = execStmt(qgen.Builder.SimpleDelete("settings", "name='url_tags'"))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return createTable("pages", "utf8mb4", "utf8mb4_general_ci",
|
||||
@ -281,14 +280,14 @@ func patch5(scanner *bufio.Scanner) error {
|
||||
"routes.AccountEditCritical": "routes.AccountEditPassword",
|
||||
"routes.AccountEditCriticalSubmit": "routes.AccountEditPasswordSubmit",
|
||||
}
|
||||
err := renameRoutes(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
e := renameRoutes(routes)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
err = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'"))
|
||||
if err != nil {
|
||||
return err
|
||||
e = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'"))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return createTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci",
|
||||
@ -333,9 +332,9 @@ func renameRoutes(routes map[string]string) error {
|
||||
}
|
||||
|
||||
for key, value := range routes {
|
||||
err := replaceTextWhere(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
e := replaceTextWhere(key, value)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,9 +369,9 @@ func patch8(scanner *bufio.Scanner) error {
|
||||
"routePanelThemesMenuItemOrderSubmit": "panel.ThemesMenuItemOrderSubmit",
|
||||
"routePanelDashboard": "panel.Dashboard",
|
||||
}
|
||||
err := renameRoutes(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
e := renameRoutes(routes)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return createTable("updates", "", "",
|
||||
@ -812,36 +811,36 @@ func patch29(scanner *bufio.Scanner) error {
|
||||
}
|
||||
|
||||
func patch30(scanner *bufio.Scanner) error {
|
||||
err := execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"registeredFor", "int", 0, false, false, "0"}, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
e := execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"registeredFor", "int", 0, false, false, "0"}, nil))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", ""))
|
||||
}
|
||||
|
||||
func patch31(scanner *bufio.Scanner) (e error) {
|
||||
addKey := func(tbl, col string, tk qgen.DBTableKey) error {
|
||||
/*err := execStmt(qgen.Builder.RemoveIndex(tbl, col))
|
||||
if err != nil {
|
||||
return err
|
||||
/*e := execStmt(qgen.Builder.RemoveIndex(tbl, col))
|
||||
if e != nil {
|
||||
return e
|
||||
}*/
|
||||
return execStmt(qgen.Builder.AddKey(tbl, col, tk))
|
||||
}
|
||||
err := addKey("topics", "title", tK{"title", "fulltext", "", false})
|
||||
if err != nil {
|
||||
return err
|
||||
e = addKey("topics", "title", tK{"title", "fulltext", "", false})
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
err = addKey("topics", "content", tK{"content", "fulltext", "", false})
|
||||
if err != nil {
|
||||
return err
|
||||
e = addKey("topics", "content", tK{"content", "fulltext", "", false})
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return addKey("replies", "content", tK{"content", "fulltext", "", false})
|
||||
}
|
||||
|
||||
func createTable(tbl, charset, collation string, cols []tC, keys []tK) error {
|
||||
err := execStmt(qgen.Builder.DropTable(tbl))
|
||||
if err != nil {
|
||||
return err
|
||||
e := execStmt(qgen.Builder.DropTable(tbl))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return execStmt(qgen.Builder.CreateTable(tbl, charset, collation, cols, keys))
|
||||
}
|
||||
@ -934,9 +933,31 @@ func patch34(scanner *bufio.Scanner) error {
|
||||
}
|
||||
|
||||
func patch35(scanner *bufio.Scanner) error {
|
||||
err := execStmt(qgen.Builder.AddColumn("topics", tC{"weekEvenViews", "int", 0, false, false, "0"}, nil))
|
||||
if err != nil {
|
||||
return err
|
||||
e := execStmt(qgen.Builder.AddColumn("topics", tC{"weekEvenViews", "int", 0, false, false, "0"}, nil))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return execStmt(qgen.Builder.AddColumn("topics", tC{"weekOddViews", "int", 0, false, false, "0"}, nil))
|
||||
}
|
||||
|
||||
func patch36(scanner *bufio.Scanner) error {
|
||||
e := createTable("forums_actions", "utf8mb4", "utf8mb4_general_ci",
|
||||
[]tC{
|
||||
{"faid", "int", 0, false, true, ""},
|
||||
{"fid", "int", 0, false, false, ""},
|
||||
bcol("runOnTopicCreation", false),
|
||||
{"runDaysAfterTopicCreation", "int", 0, false, false, "0"},
|
||||
{"runDaysAfterTopicLastReply", "int", 0, false, false, "0"},
|
||||
ccol("action", 50, ""),
|
||||
ccol("extra", 200, "''"),
|
||||
},
|
||||
[]tK{
|
||||
{"faid", "primary", "", false},
|
||||
},
|
||||
)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
//qgen.Install.SimpleInsert("settings", "name, content, type, constraints", "'activation_type','1','list','1-3'")
|
||||
return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type", "'avatar_visibility','0','list','0-1'"))
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ func (b *accDeleteBuilder) DateOlderThan(col string, quantity int, unit string)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *accDeleteBuilder) DateOlderThanQ(col, unit string) *accDeleteBuilder {
|
||||
b.dateCutoff = &dateCutoff{col, 0, unit, 11}
|
||||
return b
|
||||
}
|
||||
|
||||
/*func (b *accDeleteBuilder) Prepare() *sql.Stmt {
|
||||
return b.build.SimpleDelete(b.table, b.where)
|
||||
}*/
|
||||
@ -87,6 +92,11 @@ func (b *accUpdateBuilder) DateOlderThan(col string, quantity int, unit string)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *accUpdateBuilder) DateOlderThanQ(col, unit string) *accUpdateBuilder {
|
||||
b.up.dateCutoff = &dateCutoff{col, 0, unit, 11}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *accUpdateBuilder) WhereQ(sel *selectPrebuilder) *accUpdateBuilder {
|
||||
b.up.whereSubQuery = sel
|
||||
return b
|
||||
@ -98,14 +108,24 @@ func (b *accUpdateBuilder) Prepare() *sql.Stmt {
|
||||
}
|
||||
return b.build.prepare(b.build.adapter.SimpleUpdate(b.up))
|
||||
}
|
||||
func (b *accUpdateBuilder) Stmt() *sql.Stmt {
|
||||
if b.up.whereSubQuery != nil {
|
||||
return b.build.prepare(b.build.adapter.SimpleUpdateSelect(b.up))
|
||||
}
|
||||
return b.build.prepare(b.build.adapter.SimpleUpdate(b.up))
|
||||
}
|
||||
|
||||
func (b *accUpdateBuilder) Exec(args ...interface{}) (res sql.Result, err error) {
|
||||
query, err := b.build.adapter.SimpleUpdate(b.up)
|
||||
q, err := b.build.adapter.SimpleUpdate(b.up)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
//fmt.Println("query:", query)
|
||||
return b.build.exec(query, args...)
|
||||
//fmt.Println("q:", q)
|
||||
return b.build.exec(q, args...)
|
||||
}
|
||||
|
||||
type AccBuilder interface {
|
||||
Prepare() *sql.Stmt
|
||||
}
|
||||
|
||||
type AccSelectBuilder struct {
|
||||
@ -147,8 +167,8 @@ func (b *AccSelectBuilder) In(col string, inList []int) *AccSelectBuilder {
|
||||
|
||||
// TODO: Optimise this
|
||||
where := col + " IN("
|
||||
for _, item := range inList {
|
||||
where += strconv.Itoa(item) + ","
|
||||
for _, it := range inList {
|
||||
where += strconv.Itoa(it) + ","
|
||||
}
|
||||
where = where[:len(where)-1] + ")"
|
||||
if b.where != "" {
|
||||
@ -217,6 +237,15 @@ func (b *AccSelectBuilder) Prepare() *sql.Stmt {
|
||||
return b.build.SimpleSelect(b.table, b.columns, b.where, b.orderby, b.limit)
|
||||
}
|
||||
|
||||
func (b *AccSelectBuilder) Stmt() *sql.Stmt {
|
||||
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
|
||||
if b.dateCutoff != nil || b.inChain != nil {
|
||||
selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
|
||||
return b.build.prepare(b.build.GetAdapter().ComplexSelect(selectBuilder))
|
||||
}
|
||||
return b.build.SimpleSelect(b.table, b.columns, b.where, b.orderby, b.limit)
|
||||
}
|
||||
|
||||
func (b *AccSelectBuilder) ComplexPrepare() *sql.Stmt {
|
||||
selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
|
||||
return b.build.prepare(b.build.GetAdapter().ComplexSelect(selectBuilder))
|
||||
|
@ -199,6 +199,8 @@ func panelRoutes() *RouteGroup {
|
||||
Action("panel.ForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extraData"),
|
||||
View("panel.ForumsEditPermsAdvance", "/panel/forums/edit/perms/", "extraData"),
|
||||
Action("panel.ForumsEditPermsAdvanceSubmit", "/panel/forums/edit/perms/adv/submit/", "extraData"),
|
||||
Action("panel.ForumsEditActionCreateSubmit", "/panel/forums/action/create/submit/", "extraData"),
|
||||
Action("panel.ForumsEditActionDeleteSubmit", "/panel/forums/action/delete/submit/", "extraData"),
|
||||
|
||||
View("panel.Settings", "/panel/settings/"),
|
||||
View("panel.SettingEdit", "/panel/settings/edit/", "extraData"),
|
||||
|
@ -18,10 +18,10 @@ func Debug(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
|
||||
|
||||
goVersion := runtime.Version()
|
||||
dbVersion := qgen.Builder.DbVersion()
|
||||
upDuration := time.Since(c.StartTime)
|
||||
hours := int(upDuration.Hours())
|
||||
mins := int(upDuration.Minutes())
|
||||
secs := int(upDuration.Seconds())
|
||||
upDur := time.Since(c.StartTime)
|
||||
hours := int(upDur.Hours())
|
||||
mins := int(upDur.Minutes())
|
||||
secs := int(upDur.Seconds())
|
||||
var uptime string
|
||||
if hours > 24 {
|
||||
days := hours / 24
|
||||
@ -44,6 +44,7 @@ func Debug(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
|
||||
// TODO: Fetch the adapter from Builder rather than getting it from a global?
|
||||
goroutines := runtime.NumGoroutine()
|
||||
cpus := runtime.NumCPU()
|
||||
httpConns := c.ConnWatch.Count()
|
||||
|
||||
debugTasks := c.DebugPageTasks{c.ScheduledHalfSecondTaskCount(), c.ScheduledSecondTaskCount(), c.ScheduledFifteenMinuteTaskCount(), c.ScheduledHourTaskCount(), c.ShutdownTaskCount()}
|
||||
var memStats runtime.MemStats
|
||||
@ -51,20 +52,17 @@ func Debug(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
|
||||
|
||||
var tlen, ulen, rlen int
|
||||
var tcap, ucap, rcap int
|
||||
tcache := c.Topics.GetCache()
|
||||
if tcache != nil {
|
||||
tlen = tcache.Length()
|
||||
tcap = tcache.GetCapacity()
|
||||
tc := c.Topics.GetCache()
|
||||
if tc != nil {
|
||||
tlen, tcap = tc.Length(), tc.GetCapacity()
|
||||
}
|
||||
ucache := c.Users.GetCache()
|
||||
if ucache != nil {
|
||||
ulen = ucache.Length()
|
||||
ucap = ucache.GetCapacity()
|
||||
uc := c.Users.GetCache()
|
||||
if uc != nil {
|
||||
ulen, ucap = uc.Length(), uc.GetCapacity()
|
||||
}
|
||||
rcache := c.Rstore.GetCache()
|
||||
if rcache != nil {
|
||||
rlen = rcache.Length()
|
||||
rcap = rcache.GetCapacity()
|
||||
rc := c.Rstore.GetCache()
|
||||
if rc != nil {
|
||||
rlen, rcap = rc.Length(), rc.GetCapacity()
|
||||
}
|
||||
topicListThawed := c.TopicListThaw.Thawed()
|
||||
|
||||
@ -121,12 +119,13 @@ func Debug(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
|
||||
if fErr != nil {
|
||||
return c.InternalError(fErr, w, r)
|
||||
}
|
||||
// TODO: How can we measure this without freezing up the entire page?
|
||||
//gitSize, _ := c.DirSize("./.git")
|
||||
gitSize := 0
|
||||
|
||||
debugDisk := c.DebugPageDisk{staticSize, attachSize, uploadsSize, logsSize, backupsSize, gitSize}
|
||||
|
||||
pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, debugTasks, memStats, debugCache, debugDatabase, debugDisk}
|
||||
pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, httpConns, debugTasks, memStats, debugCache, debugDatabase, debugDisk}
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_dashboard_right", "debug_page", "panel_debug", pi})
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,7 @@ func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.Rout
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Move this even earlier?
|
||||
js := r.PostFormValue("js") == "1"
|
||||
if !u.Perms.ManageForums {
|
||||
return c.NoPermissionsJSQ(w, r, u, js)
|
||||
@ -187,14 +188,14 @@ func ForumsEdit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string)
|
||||
}
|
||||
basePage.Header.AddScriptAsync("panel_forum_edit.js")
|
||||
|
||||
forum, err := c.Forums.Get(fid)
|
||||
f, err := c.Forums.Get(fid)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u)
|
||||
} else if err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
}
|
||||
if forum.Preset == "" {
|
||||
forum.Preset = "custom"
|
||||
if f.Preset == "" {
|
||||
f.Preset = "custom"
|
||||
}
|
||||
|
||||
glist, err := c.Groups.GetAll()
|
||||
@ -221,7 +222,16 @@ func ForumsEdit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string)
|
||||
basePage.AddNotice("panel_forum_updated")
|
||||
}
|
||||
|
||||
pi := c.PanelEditForumPage{basePage, forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist}
|
||||
falist, e := c.ForumActionStore.GetInForum(f.ID)
|
||||
if err != sql.ErrNoRows && e != nil {
|
||||
return c.InternalError(e, w, r)
|
||||
}
|
||||
afalist := make([]*c.ForumActionAction, len(falist))
|
||||
for i, faitem := range falist {
|
||||
afalist[i] = &c.ForumActionAction{faitem, c.ConvActToString(faitem.Action)}
|
||||
}
|
||||
|
||||
pi := c.PanelEditForumPage{basePage, f.ID, f.Name, f.Desc, f.Active, f.Preset, gplist, afalist}
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_forum_edit", &pi})
|
||||
}
|
||||
|
||||
@ -290,7 +300,7 @@ func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sf
|
||||
return c.LocalErrorJSQ("Invalid Group ID", w, r, u, js)
|
||||
}
|
||||
|
||||
forum, err := c.Forums.Get(fid)
|
||||
f, err := c.Forums.Get(fid)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.LocalErrorJSQ("This forum doesn't exist", w, r, u, js)
|
||||
} else if err != nil {
|
||||
@ -298,7 +308,7 @@ func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sf
|
||||
}
|
||||
|
||||
permPreset := c.StripInvalidGroupForumPreset(r.PostFormValue("perm_preset"))
|
||||
err = forum.SetPreset(permPreset, gid)
|
||||
err = f.SetPreset(permPreset, gid)
|
||||
if err != nil {
|
||||
return c.LocalErrorJSQ(err.Error(), w, r, u, js)
|
||||
}
|
||||
@ -311,23 +321,20 @@ func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sf
|
||||
}
|
||||
|
||||
// A helper function for the Advanced portion of the Forum Perms Editor
|
||||
func forumPermsExtractDash(paramList string) (fid, gid int, err error) {
|
||||
func forumPermsExtractDash(paramList string) (fid, gid int, e error) {
|
||||
params := strings.Split(paramList, "-")
|
||||
if len(params) != 2 {
|
||||
return fid, gid, errors.New("Parameter count mismatch")
|
||||
}
|
||||
|
||||
fid, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
fid, e = strconv.Atoi(params[0])
|
||||
if e != nil {
|
||||
return fid, gid, errors.New("The provided Forum ID is not a valid number.")
|
||||
}
|
||||
|
||||
gid, err = strconv.Atoi(params[1])
|
||||
if err != nil {
|
||||
err = errors.New("The provided Group ID is not a valid number.")
|
||||
gid, e = strconv.Atoi(params[1])
|
||||
if e != nil {
|
||||
e = errors.New("The provided Group ID is not a valid number.")
|
||||
}
|
||||
|
||||
return fid, gid, err
|
||||
return fid, gid, e
|
||||
}
|
||||
|
||||
func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, u *c.User, paramList string) c.RouteError {
|
||||
@ -403,7 +410,7 @@ func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, u *c.U
|
||||
return c.LocalError(err.Error(), w, r, u)
|
||||
}
|
||||
|
||||
forum, err := c.Forums.Get(fid)
|
||||
f, err := c.Forums.Get(fid)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u)
|
||||
} else if err != nil {
|
||||
@ -417,25 +424,24 @@ func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, u *c.U
|
||||
return c.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
extractPerm := func(name string) bool {
|
||||
ep := func(name string) bool {
|
||||
pvalue := r.PostFormValue("perm-" + name)
|
||||
return (pvalue == "1")
|
||||
}
|
||||
|
||||
// TODO: Generate this code?
|
||||
fp.ViewTopic = extractPerm("ViewTopic")
|
||||
fp.LikeItem = extractPerm("LikeItem")
|
||||
fp.CreateTopic = extractPerm("CreateTopic")
|
||||
fp.EditTopic = extractPerm("EditTopic")
|
||||
fp.DeleteTopic = extractPerm("DeleteTopic")
|
||||
fp.CreateReply = extractPerm("CreateReply")
|
||||
fp.EditReply = extractPerm("EditReply")
|
||||
fp.DeleteReply = extractPerm("DeleteReply")
|
||||
fp.PinTopic = extractPerm("PinTopic")
|
||||
fp.CloseTopic = extractPerm("CloseTopic")
|
||||
fp.MoveTopic = extractPerm("MoveTopic")
|
||||
fp.ViewTopic = ep("ViewTopic")
|
||||
fp.LikeItem = ep("LikeItem")
|
||||
fp.CreateTopic = ep("CreateTopic")
|
||||
fp.EditTopic = ep("EditTopic")
|
||||
fp.DeleteTopic = ep("DeleteTopic")
|
||||
fp.CreateReply = ep("CreateReply")
|
||||
fp.EditReply = ep("EditReply")
|
||||
fp.DeleteReply = ep("DeleteReply")
|
||||
fp.PinTopic = ep("PinTopic")
|
||||
fp.CloseTopic = ep("CloseTopic")
|
||||
fp.MoveTopic = ep("MoveTopic")
|
||||
|
||||
err = forum.SetPerms(&fp, "custom", gid)
|
||||
err = f.SetPerms(&fp, "custom", gid)
|
||||
if err != nil {
|
||||
return c.LocalErrorJSQ(err.Error(), w, r, u, js)
|
||||
}
|
||||
@ -446,3 +452,107 @@ func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, u *c.U
|
||||
|
||||
return successRedirect("/panel/forums/edit/perms/"+strconv.Itoa(fid)+"-"+strconv.Itoa(gid)+"?updated=1", w, r, js)
|
||||
}
|
||||
|
||||
func ForumsEditActionDeleteSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sfaid string) c.RouteError {
|
||||
_, ferr := c.SimplePanelUserCheck(w, r, u)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Should we split this permission?
|
||||
if !u.Perms.ManageForums {
|
||||
return c.NoPermissions(w, r, u)
|
||||
}
|
||||
js := r.PostFormValue("js") == "1"
|
||||
|
||||
faid, e := strconv.Atoi(sfaid)
|
||||
if e != nil {
|
||||
return c.LocalError("The forum action ID is not a valid integer.", w, r, u)
|
||||
}
|
||||
e = c.ForumActionStore.Delete(faid)
|
||||
if e != nil {
|
||||
return c.InternalError(e, w, r)
|
||||
}
|
||||
|
||||
fid, e := strconv.Atoi(r.FormValue("ret"))
|
||||
if e != nil {
|
||||
return c.LocalError("The forum action ID is not a valid integer.", w, r, u)
|
||||
}
|
||||
if !c.Forums.Exists(fid) {
|
||||
return c.LocalError("The target forum doesn't exist.", w, r, u)
|
||||
}
|
||||
|
||||
return successRedirect("/panel/forums/edit/"+strconv.Itoa(fid)+"?updated=1", w, r, js)
|
||||
}
|
||||
|
||||
func ForumsEditActionCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError {
|
||||
_, ferr := c.SimplePanelUserCheck(w, r, u)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Should we split this permission?
|
||||
if !u.Perms.ManageForums {
|
||||
return c.NoPermissions(w, r, u)
|
||||
}
|
||||
js := r.PostFormValue("js") == "1"
|
||||
|
||||
fid, e := strconv.Atoi(sfid)
|
||||
if e != nil {
|
||||
return c.LocalError("The provided Forum ID is not a valid number.", w, r, u)
|
||||
}
|
||||
if !c.Forums.Exists(fid) {
|
||||
return c.LocalError("This forum does not exist", w, r, u)
|
||||
}
|
||||
|
||||
runOnTopicCreation := r.PostFormValue("action_run_on_topic_creation") == "1"
|
||||
|
||||
f := func(s string) (int, c.RouteError) {
|
||||
i, e := strconv.Atoi(r.PostFormValue(s))
|
||||
if e != nil {
|
||||
return i, c.LocalError(s+" is not a valid integer.", w, r, u)
|
||||
}
|
||||
if i < 0 {
|
||||
return i, c.LocalError(s+" cannot be less than 0", w, r, u)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
runDaysAfterTopicCreation, re := f("action_run_days_after_topic_creation")
|
||||
if re != nil {
|
||||
return re
|
||||
}
|
||||
runDaysAfterTopicLastReply, re := f("action_run_days_after_topic_last_reply")
|
||||
if re != nil {
|
||||
return re
|
||||
}
|
||||
|
||||
action := r.PostFormValue("action_action")
|
||||
aint := c.ConvStringToAct(action)
|
||||
if aint == -1 {
|
||||
return c.LocalError("invalid action", w, r, u)
|
||||
}
|
||||
|
||||
extra := r.PostFormValue("action_extra")
|
||||
switch aint {
|
||||
case c.ForumActionMove:
|
||||
conv, e := strconv.Atoi(extra)
|
||||
if e != nil {
|
||||
return c.LocalError("action_extra is not a valid integer.", w, r, u)
|
||||
}
|
||||
extra = strconv.Itoa(conv)
|
||||
default:
|
||||
extra = ""
|
||||
}
|
||||
|
||||
_, e = c.ForumActionStore.Add(&c.ForumAction{
|
||||
Forum: fid,
|
||||
RunOnTopicCreation: runOnTopicCreation,
|
||||
RunDaysAfterTopicCreation: runDaysAfterTopicCreation,
|
||||
RunDaysAfterTopicLastReply: runDaysAfterTopicLastReply,
|
||||
Action: aint,
|
||||
Extra: extra,
|
||||
})
|
||||
if e != nil {
|
||||
return c.InternalError(e, w, r)
|
||||
}
|
||||
|
||||
return successRedirect("/panel/forums/edit/"+strconv.Itoa(fid)+"?updated=1", w, r, js)
|
||||
}
|
||||
|
@ -20,9 +20,9 @@ func WordFilters(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError
|
||||
}
|
||||
|
||||
// TODO: What if this list gets too long?
|
||||
filters, err := c.WordFilters.GetAll()
|
||||
if err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
filters, e := c.WordFilters.GetAll()
|
||||
if e != nil {
|
||||
return c.InternalError(e, w, r)
|
||||
}
|
||||
|
||||
pi := c.PanelPage{basePage, tList, filters}
|
||||
@ -48,13 +48,13 @@ func WordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User)
|
||||
// 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
|
||||
replace := strings.TrimSpace(r.PostFormValue("replace"))
|
||||
|
||||
wfid, err := c.WordFilters.Create(find, replace)
|
||||
if err != nil {
|
||||
return c.InternalErrorJSQ(err, w, r, js)
|
||||
wfid, e := c.WordFilters.Create(find, replace)
|
||||
if e != nil {
|
||||
return c.InternalErrorJSQ(e, w, r, js)
|
||||
}
|
||||
err = c.AdminLogs.Create("create", wfid, "word_filter", u.GetIP(), u.ID)
|
||||
if err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
e = c.AdminLogs.Create("create", wfid, "word_filter", u.GetIP(), u.ID)
|
||||
if e != nil {
|
||||
return c.InternalError(e, w, r)
|
||||
}
|
||||
|
||||
return successRedirect("/panel/settings/word-filters/", w, r, js)
|
||||
|
@ -13,8 +13,8 @@ import (
|
||||
|
||||
func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList {
|
||||
wsTopicList := make([]*c.WsTopicsRow, len(topicList))
|
||||
for i, topicRow := range topicList {
|
||||
wsTopicList[i] = topicRow.WebSockets()
|
||||
for i, tr := range topicList {
|
||||
wsTopicList[i] = tr.WebSockets()
|
||||
}
|
||||
return &c.WsTopicList{wsTopicList, lastPage, 0}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ INSERT INTO [settings] ([name],[content],[type]) VALUES ('megapost_min_words','1
|
||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('meta_desc','','html-attribute');
|
||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('rapid_loading','1','bool');
|
||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('google_site_verify','','html-attribute');
|
||||
INSERT INTO [settings] ([name],[content],[type],[constraints]) VALUES ('avatar_visibility','0','list','0-1');
|
||||
INSERT INTO [themes] ([uname],[default]) VALUES ('cosora',1);
|
||||
INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,1);
|
||||
INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"UseConvosOnlyWithMod":true,"CreateProfileReply":true,"AutoEmbed":true,"AutoLink":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin');
|
||||
@ -27,8 +28,8 @@ INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (3,2,'{"View
|
||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (4,2,'{"ViewTopic":true}');
|
||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (5,2,'{"ViewTopic":true}');
|
||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (6,2,'{"ViewTopic":true}');
|
||||
INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[lastReplyBy],[createdBy],[parentID],[ip]) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',GETUTCDATE(),GETUTCDATE(),1,1,2,'::1');
|
||||
INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[createdBy],[lastUpdated],[lastEdit],[lastEditBy],[ip]) VALUES (1,'A reply!','A reply!',GETUTCDATE(),1,GETUTCDATE(),0,0,'::1');
|
||||
INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[lastReplyBy],[createdBy],[parentID],[ip]) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',GETUTCDATE(),GETUTCDATE(),1,1,2,'');
|
||||
INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[createdBy],[lastUpdated],[lastEdit],[lastEditBy],[ip]) VALUES (1,'A reply!','A reply!',GETUTCDATE(),1,GETUTCDATE(),0,0,'');
|
||||
INSERT INTO [menus] () VALUES ();
|
||||
INSERT INTO [menu_items] ([mid],[name],[htmlID],[position],[path],[aria],[tooltip],[order]) VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
||||
INSERT INTO [menu_items] ([mid],[name],[htmlID],[cssClass],[position],[path],[aria],[tooltip],[order]) VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
||||
|
@ -13,6 +13,7 @@ INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('megapost_min_words','10
|
||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');
|
||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('rapid_loading','1','bool');
|
||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('google_site_verify','','html-attribute');
|
||||
INSERT INTO `settings`(`name`,`content`,`type`,`constraints`) VALUES ('avatar_visibility','0','list','0-1');
|
||||
INSERT INTO `themes`(`uname`,`default`) VALUES ('cosora',1);
|
||||
INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
||||
INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"UseConvosOnlyWithMod":true,"CreateProfileReply":true,"AutoEmbed":true,"AutoLink":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin');
|
||||
@ -35,8 +36,8 @@ INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (3,2,'{"ViewT
|
||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (4,2,'{"ViewTopic":true}');
|
||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (5,2,'{"ViewTopic":true}');
|
||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (6,2,'{"ViewTopic":true}');
|
||||
INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`createdBy`,`parentID`,`ip`) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1');
|
||||
INSERT INTO `replies`(`tid`,`content`,`parsed_content`,`createdAt`,`createdBy`,`lastUpdated`,`lastEdit`,`lastEditBy`,`ip`) VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1');
|
||||
INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`createdBy`,`parentID`,`ip`) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'');
|
||||
INSERT INTO `replies`(`tid`,`content`,`parsed_content`,`createdAt`,`createdBy`,`lastUpdated`,`lastEdit`,`lastEditBy`,`ip`) VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'');
|
||||
INSERT INTO `menus`() VALUES ();
|
||||
INSERT INTO `menu_items`(`mid`,`name`,`htmlID`,`position`,`path`,`aria`,`tooltip`,`order`) VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
||||
INSERT INTO `menu_items`(`mid`,`name`,`htmlID`,`cssClass`,`position`,`path`,`aria`,`tooltip`,`order`) VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
||||
|
10
schema/mysql/query_forums_actions.sql
Normal file
10
schema/mysql/query_forums_actions.sql
Normal file
@ -0,0 +1,10 @@
|
||||
CREATE TABLE `forums_actions` (
|
||||
`faid` int not null AUTO_INCREMENT,
|
||||
`fid` int not null,
|
||||
`runOnTopicCreation` boolean DEFAULT 0 not null,
|
||||
`runDaysAfterTopicCreation` int DEFAULT 0 not null,
|
||||
`runDaysAfterTopicLastReply` int DEFAULT 0 not null,
|
||||
`action` varchar(50) not null,
|
||||
`extra` varchar(200) DEFAULT '' not null,
|
||||
primary key(`faid`)
|
||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
@ -5,6 +5,7 @@ INSERT INTO "settings"("name","content","type") VALUES ('megapost_min_words','10
|
||||
INSERT INTO "settings"("name","content","type") VALUES ('meta_desc','','html-attribute');
|
||||
INSERT INTO "settings"("name","content","type") VALUES ('rapid_loading','1','bool');
|
||||
INSERT INTO "settings"("name","content","type") VALUES ('google_site_verify','','html-attribute');
|
||||
INSERT INTO "settings"("name","content","type","constraints") VALUES ('avatar_visibility','0','list','0-1');
|
||||
INSERT INTO "themes"("uname","default") VALUES ('cosora',1);
|
||||
INSERT INTO "emails"("email","uid","validated") VALUES ('admin@localhost',1,1);
|
||||
INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"UseConvosOnlyWithMod":true,"CreateProfileReply":true,"AutoEmbed":true,"AutoLink":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin');
|
||||
@ -27,8 +28,8 @@ INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (3,2,'{"ViewT
|
||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (4,2,'{"ViewTopic":true}');
|
||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (5,2,'{"ViewTopic":true}');
|
||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (6,2,'{"ViewTopic":true}');
|
||||
INSERT INTO "topics"("title","content","parsed_content","createdAt","lastReplyAt","lastReplyBy","createdBy","parentID","ip") VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1');
|
||||
INSERT INTO "replies"("tid","content","parsed_content","createdAt","createdBy","lastUpdated","lastEdit","lastEditBy","ip") VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1');
|
||||
INSERT INTO "topics"("title","content","parsed_content","createdAt","lastReplyAt","lastReplyBy","createdBy","parentID","ip") VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'');
|
||||
INSERT INTO "replies"("tid","content","parsed_content","createdAt","createdBy","lastUpdated","lastEdit","lastEditBy","ip") VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'');
|
||||
INSERT INTO "menus"() VALUES ();
|
||||
INSERT INTO "menu_items"("mid","name","htmlID","position","path","aria","tooltip","order") VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
||||
INSERT INTO "menu_items"("mid","name","htmlID","cssClass","position","path","aria","tooltip","order") VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
||||
|
@ -10,10 +10,12 @@ CREATE TABLE "users" (
|
||||
`lastActiveAt` timestamp not null,
|
||||
`session` varchar (200) DEFAULT '' not null,
|
||||
`last_ip` varchar (200) DEFAULT '' not null,
|
||||
`profile_comments` int DEFAULT 0 not null,
|
||||
`who_can_convo` int DEFAULT 0 not null,
|
||||
`enable_embeds` int DEFAULT -1 not null,
|
||||
`email` varchar (200) DEFAULT '' not null,
|
||||
`avatar` varchar (100) DEFAULT '' not null,
|
||||
`message` text DEFAULT '' not null,
|
||||
`message` text not null,
|
||||
`url_prefix` varchar (20) DEFAULT '' not null,
|
||||
`url_name` varchar (100) DEFAULT '' not null,
|
||||
`level` smallint DEFAULT 0 not null,
|
||||
|
@ -5,6 +5,6 @@ CREATE TABLE "widgets" (
|
||||
`type` varchar (100) not null,
|
||||
`active` boolean DEFAULT 0 not null,
|
||||
`location` varchar (100) not null,
|
||||
`data` text DEFAULT '' not null,
|
||||
`data` text not null,
|
||||
primary key(`wid`)
|
||||
);
|
@ -12,7 +12,7 @@
|
||||
{{if .Desc}}
|
||||
<span class="rowsmall"itemprop="description">{{.Desc}}</span>
|
||||
{{else}}
|
||||
<span class="rowsmall forum_nodesc">{{lang "forums_no_description"}}</span>
|
||||
<span class="rowsmall forum_nodesc">{{lang "forums_no_desc"}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
<span class="forum_right shift_right">
|
||||
|
@ -13,16 +13,16 @@
|
||||
{{template "panel_debug_stat_head.html" "panel_debug_adapter_label"}}
|
||||
{{/** TODO: Use this for active database connections when Go 1.11 lands **/}}
|
||||
{{template "panel_debug_stat_head_q.html"}}
|
||||
{{template "panel_debug_stat.html" .OpenConns}}
|
||||
{{template "panel_debug_stat.html" .DBConns}}
|
||||
{{template "panel_debug_stat.html" .DBAdapter}}
|
||||
{{template "panel_debug_stat_q.html"}}
|
||||
|
||||
{{template "panel_debug_stat_head.html" "panel_debug_goroutine_count_label"}}
|
||||
{{template "panel_debug_stat_head.html" "panel_debug_cpu_count_label"}}
|
||||
{{template "panel_debug_stat_head_q.html"}}
|
||||
{{template "panel_debug_stat_head.html" "panel_debug_http_conns_label"}}
|
||||
{{template "panel_debug_stat.html" .Goroutines}}
|
||||
{{template "panel_debug_stat.html" .CPUs}}
|
||||
{{template "panel_debug_stat_q.html"}}
|
||||
{{template "panel_debug_stat.html" .HttpConns}}
|
||||
</div>
|
||||
{{template "panel_debug_subhead.html" "panel_debug_tasks"}}
|
||||
<div id="panel_debug"class="colstack_grid">
|
||||
|
@ -1,45 +1,45 @@
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_forum_head_suffix"}}</h1></div>
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel.forum_head_suffix"}}</h1></div>
|
||||
</div>
|
||||
<div id="panel_forum"class="colstack_item the_form">
|
||||
<form action="/panel/forums/edit/submit/{{.ID}}?s={{.CurrentUser.Session}}"method="post">
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_forum_name"}}</a></div>
|
||||
<div class="formitem"><input name="forum_name"type="text"value="{{.Name}}"placeholder="{{lang "panel_forum_name_placeholder"}}"></div>
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_name"}}</a></div>
|
||||
<div class="formitem"><input name="forum_name"type="text"value="{{.Name}}"placeholder="{{lang "panel.forum_name_placeholder"}}"></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_forum_description"}}</a></div>
|
||||
<div class="formitem"><input name="forum_desc"type="text"value="{{.Desc}}"placeholder="{{lang "panel_forum_description_placeholder"}}"></div>
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_desc"}}</a></div>
|
||||
<div class="formitem"><input name="forum_desc"type="text"value="{{.Desc}}"placeholder="{{lang "panel.forum_desc_placeholder"}}"></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_forum_active"}}</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_active"}}</a></div>
|
||||
<div class="formitem"><select name="forum_active">
|
||||
<option{{if .Active}} selected{{end}} value=1>{{lang "option_yes"}}</option>
|
||||
<option{{if not .Active}} selected{{end}} value=0>{{lang "option_no"}}</option>
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel_forum_preset"}}</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_preset"}}</a></div>
|
||||
<div class="formitem">
|
||||
<select name="forum_preset">
|
||||
<option{{if eq .Preset "all"}} selected{{end}} value="all">{{lang "panel_preset_everyone"}}</option>
|
||||
<option{{if eq .Preset "announce"}} selected{{end}} value="announce">{{lang "panel_preset_announcements"}}</option>
|
||||
<option{{if eq .Preset "members"}} selected{{end}} value="members">{{lang "panel_preset_member_only"}}</option>
|
||||
<option{{if eq .Preset "staff"}} selected{{end}} value="staff">{{lang "panel_preset_staff_only"}}</option>
|
||||
<option{{if eq .Preset "admins"}} selected{{end}} value="admins">{{lang "panel_preset_admin_only"}}</option>
|
||||
<option{{if eq .Preset "archive"}} selected{{end}} value="archive">{{lang "panel_preset_archive"}}</option>
|
||||
<option{{if eq .Preset "custom"}} selected{{end}} value="custom">{{lang "panel_preset_custom"}}</option>
|
||||
<option{{if eq .Preset "all"}} selected{{end}} value="all">{{lang "panel.preset_everyone"}}</option>
|
||||
<option{{if eq .Preset "announce"}} selected{{end}} value="announce">{{lang "panel.preset_announcements"}}</option>
|
||||
<option{{if eq .Preset "members"}} selected{{end}} value="members">{{lang "panel.preset_member_only"}}</option>
|
||||
<option{{if eq .Preset "staff"}} selected{{end}} value="staff">{{lang "panel.preset_staff_only"}}</option>
|
||||
<option{{if eq .Preset "admins"}} selected{{end}} value="admins">{{lang "panel.preset_admin_only"}}</option>
|
||||
<option{{if eq .Preset "archive"}} selected{{end}} value="archive">{{lang "panel.preset_archive"}}</option>
|
||||
<option{{if eq .Preset "custom"}} selected{{end}} value="custom">{{lang "panel.preset_custom"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="panel-button"class="formbutton form_middle_button">{{lang "panel_forum_update_button"}}</button></div>
|
||||
<div class="formitem"><button name="panel-button"class="formbutton form_middle_button">{{lang "panel.forum_update_button"}}</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<h1>{{lang "panel_forum_permissions_head"}}</h1>
|
||||
<h1>{{lang "panel.forum_permissions_head"}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="forum_quick_perms"class="colstack_item rowlist formlist the_form">
|
||||
@ -48,13 +48,76 @@
|
||||
<div class="formitem editable_parent">
|
||||
<a>{{.Group.Name}}</a>
|
||||
<input name="gid"value="{{.Group.ID}}"type="hidden"class="editable_block"data-field="gid"data-type="hidden"data-value="{{.Group.ID}}">
|
||||
<span class="edit_fields hide_on_edit rowsmall">{{lang "panel_forum_edit_button"}}</span>
|
||||
<span class="edit_fields hide_on_edit rowsmall">{{lang "panel.forum_edit_button"}}</span>
|
||||
<div class="panel_floater">
|
||||
<span data-field="perm_preset"data-type="list"data-value="{{.Preset}}" class="editable_block perm_preset perm_preset_{{.Preset}}"></span>
|
||||
<a class="panel_right_button has_inner_button show_on_edit"href="/panel/forums/edit/perms/submit/{{$.ID}}"><button class='panel_tag submit_edit'type='submit'>{{lang "panel_forum_short_update_button"}}</button></a>
|
||||
<a class="panel_right_button has_inner_button show_on_edit"href="/panel/forums/edit/perms/{{$.ID}}-{{.Group.ID}}"><button class='panel_tag'type='submit'>{{lang "panel_forum_full_edit_button"}}</button></a>
|
||||
<span data-field="perm_preset"data-type="list"data-value="{{.Preset}}"class="editable_block perm_preset perm_preset_{{.Preset}}"></span>
|
||||
<a class="panel_right_button has_inner_button show_on_edit"href="/panel/forums/edit/perms/submit/{{$.ID}}"><button class='panel_tag submit_edit'type='submit'>{{lang "panel.forum_short_update_button"}}</button></a>
|
||||
<a class="panel_right_button has_inner_button show_on_edit"href="/panel/forums/edit/perms/{{$.ID}}-{{.Group.ID}}"><button class='panel_tag'type='submit'>{{lang "panel.forum_full_edit_button"}}</button></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .Actions}}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel.forum_actions_head"}}</h1></div>
|
||||
</div>
|
||||
<div id="panel_forum_actions"class="colstack_item rowlist">
|
||||
{{range .Actions}}
|
||||
<div class="rowitem panel_compactrow editable_parent">
|
||||
<a class="panel_upshift">{{.ActionName}}{{if .RunDaysAfterTopicCreation}} - {{.RunDaysAfterTopicCreation}} days after topic creation{{end}}{{if .RunDaysAfterTopicLastReply}} - {{.RunDaysAfterTopicLastReply}} days after topic last reply{{end}}</a>
|
||||
<span class="panel_floater">
|
||||
<a href="/panel/forums/action/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}&ret={{$.ID}}"class="panel_tag panel_right_button delete_button"></a>
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<h1>{{lang "panel.forum_actions_create_head"}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="panel_forum_action_create"class="colstack_item the_form">
|
||||
<form action="/panel/forums/action/create/submit/{{.ID}}?s={{.CurrentUser.Session}}"method="post">
|
||||
<!--<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_action_run_on_topic_creation"}}</a></div>
|
||||
<div class="formitem"><select name="action_run_on_topic_creation">
|
||||
<option value=1>{{lang "option_yes"}}</option>
|
||||
<option selected value=0>{{lang "option_no"}}</option>
|
||||
</select></div>
|
||||
</div>-->
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_action_run_days_after_topic_creation"}}</a></div>
|
||||
<div class="formitem">
|
||||
<input name="action_run_days_after_topic_creation"value="0"type="number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_action_run_days_after_topic_last_reply"}}</a></div>
|
||||
<div class="formitem">
|
||||
<input name="action_run_days_after_topic_last_reply"value="0"type="number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_action_action"}}</a></div>
|
||||
<div class="formitem">
|
||||
<select name="action_action">
|
||||
<option value="delete"selected>{{lang "panel.forum_action_action_delete"}}</option>
|
||||
<option value="lock">{{lang "panel.forum_action_action_lock"}}</option>
|
||||
<option value="unlock">{{lang "panel.forum_action_action_unlock"}}</option>
|
||||
<option value="move">{{lang "panel.forum_action_action_move"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forum_action_extra"}}</a></div>
|
||||
<div class="formitem">
|
||||
<input name="action_extra"type="text">
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="panel-button"class="formbutton form_middle_button">{{lang "panel.forum_action_create_button"}}</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -1,5 +1,5 @@
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_forum_head_suffix"}}</h1></div>
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel.forum_head_suffix"}}</h1></div>
|
||||
</div>
|
||||
<form action="/panel/forums/edit/perms/adv/submit/{{.ForumID}}-{{.GroupID}}?s={{.CurrentUser.Session}}"method="post">
|
||||
<div class="colstack_item rowlist formlist the_form panel_forum_perms">
|
||||
@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</div>{{end}}
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="panel-button"class="formbutton form_middle_button">{{lang "panel_forum_update_button"}}</button></div>
|
||||
<div class="formitem"><button name="panel-button"class="formbutton form_middle_button">{{lang "panel.forum_update_button"}}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -40,8 +40,8 @@
|
||||
<div class="formitem"><input name="name" type="text" placeholder="{{lang "panel.forums_create_name"}}"></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forums_create_description_label"}}</a></div>
|
||||
<div class="formitem"><input name="desc" type="text" placeholder="{{lang "panel.forums_create_description"}}"></div>
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forums_create_desc_label"}}</a></div>
|
||||
<div class="formitem"><input name="desc" type="text" placeholder="{{lang "panel.forums_create_desc"}}"></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forums_active_label"}}</a></div>
|
||||
@ -53,13 +53,13 @@
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "panel.forums_preset_label"}}</a></div>
|
||||
<div class="formitem"><select name="preset">
|
||||
<option selected value="all">{{lang "panel_preset_everyone"}}</option>
|
||||
<option value="announce">{{lang "panel_preset_announcements"}}</option>
|
||||
<option value="members">{{lang "panel_preset_member_only"}}</option>
|
||||
<option value="staff">{{lang "panel_preset_staff_only"}}</option>
|
||||
<option value="admins">{{lang "panel_preset_admin_only"}}</option>
|
||||
<option value="archive">{{lang "panel_preset_archive"}}</option>
|
||||
<option value="custom">{{lang "panel_preset_custom"}}</option>
|
||||
<option selected value="all">{{lang "panel.preset_everyone"}}</option>
|
||||
<option value="announce">{{lang "panel.preset_announcements"}}</option>
|
||||
<option value="members">{{lang "panel.preset_member_only"}}</option>
|
||||
<option value="staff">{{lang "panel.preset_staff_only"}}</option>
|
||||
<option value="admins">{{lang "panel.preset_admin_only"}}</option>
|
||||
<option value="archive">{{lang "panel.preset_archive"}}</option>
|
||||
<option value="custom">{{lang "panel.preset_custom"}}</option>
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
|
Loading…
Reference in New Issue
Block a user