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("router_end", "w http.ResponseWriter,r *http.Request,u *User,prefix string,extraData string")
|
||||||
vhooknoret("topic_reply_row_assign", "r *ReplyUser")
|
vhooknoret("topic_reply_row_assign", "r *ReplyUser")
|
||||||
|
vhooknoret("counters_perf_tick_row", "low int64,high int64,avg int64")
|
||||||
//forums_frow_assign
|
//forums_frow_assign
|
||||||
//Hook(name string, data interface{}) interface{}
|
//Hook(name string, data interface{}) interface{}
|
||||||
/*hook := func(name, params, ret, pure string) {
|
/*hook := func(name, params, ret, pure string) {
|
||||||
|
|
|
@ -5,12 +5,12 @@ package main // import "github.com/Azareal/Gosora/hook_gen"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
|
||||||
_ "github.com/Azareal/Gosora/extend"
|
|
||||||
c "github.com/Azareal/Gosora/common"
|
|
||||||
h "github.com/Azareal/Gosora/cmd/common_hook_gen"
|
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
|
// TODO: Make sure all the errors in this file propagate upwards properly
|
||||||
|
@ -48,18 +48,19 @@ func main() {
|
||||||
|
|
||||||
imports := []string{"net/http"}
|
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) {
|
add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) {
|
||||||
var params2 string
|
|
||||||
first := true
|
first := true
|
||||||
for _, param := range strings.Split(params, ",") {
|
for _, param := range strings.Split(params, ",") {
|
||||||
if !first {
|
if !first {
|
||||||
params2 += ","
|
params2sb.WriteRune(',')
|
||||||
}
|
}
|
||||||
pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param), " ", " "), " ")
|
pspl := strings.Split(strings.ReplaceAll(strings.TrimSpace(param), " ", " "), " ")
|
||||||
params2 += pspl[0]
|
params2sb.WriteString(pspl[0])
|
||||||
first = false
|
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.AddHooks(add)
|
||||||
|
|
|
@ -90,16 +90,16 @@ func (s *DefaultActivityStream) AidsByParams(event string, elementID int, elemen
|
||||||
return aids, rows.Err()
|
return aids, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, err error) {
|
func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, e error) {
|
||||||
rows, err := s.aidsByParamsExtra.Query(event, elementID, elementType, extra)
|
rows, e := s.aidsByParamsExtra.Query(event, elementID, elementType, extra)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return nil, err
|
return nil, e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var aid int
|
var aid int
|
||||||
if err := rows.Scan(&aid); err != nil {
|
if e := rows.Scan(&aid); e != nil {
|
||||||
return nil, err
|
return nil, e
|
||||||
}
|
}
|
||||||
aids = append(aids, aid)
|
aids = append(aids, aid)
|
||||||
}
|
}
|
||||||
|
@ -109,9 +109,9 @@ func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, e
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
// Count returns the total number of activity stream items
|
// Count returns the total number of activity stream items
|
||||||
func (s *DefaultActivityStream) Count() (count int) {
|
func (s *DefaultActivityStream) Count() (count int) {
|
||||||
err := s.count.QueryRow().Scan(&count)
|
e := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
LogError(err)
|
LogError(e)
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -291,3 +293,22 @@ func inqbuildstr(strs []string) ([]interface{}, string) {
|
||||||
}
|
}
|
||||||
return idList, sb.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) {
|
func (co *Conversation) PostsCount() (count int) {
|
||||||
err := convoStmts.countPosts.QueryRow(co.ID).Scan(&count)
|
return Countf(convoStmts.countPosts, co.ID)
|
||||||
if err != nil {
|
|
||||||
LogError(err)
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *Conversation) Uids() (ids []int, err error) {
|
func (co *Conversation) Uids() (ids []int, err error) {
|
||||||
rows, err := convoStmts.getUsers.Query(co.ID)
|
rows, e := convoStmts.getUsers.Query(co.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return nil, err
|
return nil, e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var id int
|
var id int
|
||||||
err := rows.Scan(&id)
|
if e := rows.Scan(&id); e != nil {
|
||||||
if err != nil {
|
return nil, e
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
ids = append(ids, id)
|
ids = append(ids, id)
|
||||||
}
|
}
|
||||||
|
@ -111,12 +105,7 @@ func (co *Conversation) Uids() (ids []int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *Conversation) Has(uid int) (in bool) {
|
func (co *Conversation) Has(uid int) (in bool) {
|
||||||
var count int
|
return Countf(convoStmts.has, uid, co.ID) > 0
|
||||||
err := convoStmts.has.QueryRow(uid, co.ID).Scan(&count)
|
|
||||||
if err != nil {
|
|
||||||
LogError(err)
|
|
||||||
}
|
|
||||||
return count > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *Conversation) Update() error {
|
func (co *Conversation) Update() error {
|
||||||
|
@ -247,7 +236,7 @@ func (s *DefaultConversationStore) GetUserExtra(uid, offset int) (cos []*Convers
|
||||||
cmap[co.ID] = &ConversationExtra{co, nil}
|
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
|
var q string
|
||||||
idList := make([]interface{}, len(raw))
|
idList := make([]interface{}, len(raw))
|
||||||
for i, co := range raw {
|
for i, co := range raw {
|
||||||
|
|
|
@ -51,21 +51,22 @@ func (co *DefaultPerfCounter) Tick() error {
|
||||||
b.Unlock()
|
b.Unlock()
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
for _, b := range co.buckets {
|
|
||||||
var low int64
|
var low int64
|
||||||
|
hTbl := c.GetHookTable()
|
||||||
|
for _, b := range co.buckets {
|
||||||
b.low.Lock()
|
b.low.Lock()
|
||||||
low = b.low.counter
|
low, b.low.counter = b.low.counter, math.MaxInt64
|
||||||
b.low.counter = math.MaxInt64
|
|
||||||
b.low.Unlock()
|
b.low.Unlock()
|
||||||
if low == math.MaxInt64 {
|
if low == math.MaxInt64 {
|
||||||
low = 0
|
low = 0
|
||||||
}
|
}
|
||||||
high := getCounter(b.high)
|
high := getCounter(b.high)
|
||||||
avg := getCounter(b.avg)
|
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?
|
e := co.insertChunk(low, high, avg) // TODO: Bulk insert for speed?
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return errors.Wrap(errors.WithStack(err), "perf counter")
|
return errors.Wrap(errors.WithStack(e), "perf counter")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -76,8 +77,8 @@ func (co *DefaultPerfCounter) insertChunk(low, high, avg int64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
c.DebugLogf("Inserting a pchunk with low %d, high %d, avg %d", low, high, avg)
|
c.DebugLogf("Inserting a pchunk with low %d, high %d, avg %d", low, high, avg)
|
||||||
_, err := co.insert.Exec(low, high, avg)
|
_, e := co.insert.Exec(low, high, avg)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *DefaultPerfCounter) Push(dur time.Duration /*,_ bool*/) {
|
func (co *DefaultPerfCounter) Push(dur time.Duration /*,_ bool*/) {
|
||||||
|
|
|
@ -126,6 +126,8 @@ var hookTable = &HookTable{
|
||||||
|
|
||||||
"tasks_tick_topic_list": nil,
|
"tasks_tick_topic_list": nil,
|
||||||
"tasks_tick_widget_wol": nil,
|
"tasks_tick_widget_wol": nil,
|
||||||
|
|
||||||
|
"counters_perf_tick_row": nil,
|
||||||
},
|
},
|
||||||
map[string][]func(string) string{
|
map[string][]func(string) string{
|
||||||
"preparse_preassign": nil,
|
"preparse_preassign": nil,
|
||||||
|
|
|
@ -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?
|
// TODO: Optimise this?
|
||||||
func (s *MemoryForumPermsStore) ReloadAll() error {
|
func (s *MemoryForumPermsStore) ReloadAll() error {
|
||||||
DebugLog("Reloading the forum perms")
|
DebugLog("Reloading the forum perms")
|
||||||
fids, err := Forums.GetAllIDs()
|
fids, e := Forums.GetAllIDs()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
for _, fid := range fids {
|
for _, fid := range fids {
|
||||||
if e := s.reload(fid); e != nil {
|
if e := s.reload(fid); e != nil {
|
||||||
|
@ -68,13 +68,13 @@ func (s *MemoryForumPermsStore) ReloadAll() error {
|
||||||
return nil
|
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))
|
DebugDetail("perms: ", string(perms))
|
||||||
pperms = BlankForumPerms()
|
pperms = BlankForumPerms()
|
||||||
err = json.Unmarshal(perms, &pperms)
|
e = json.Unmarshal(perms, &pperms)
|
||||||
pperms.ExtData = make(map[string]bool)
|
pperms.ExtData = make(map[string]bool)
|
||||||
pperms.Overrides = true
|
pperms.Overrides = true
|
||||||
return pperms, err
|
return pperms, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MemoryForumPermsStore) Reload(fid int) error {
|
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: Check if the forum exists?
|
||||||
// TODO: Fix the races
|
// TODO: Fix the races
|
||||||
func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, err error) {
|
func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, e error) {
|
||||||
fPermsPtr, err := s.Get(fid, gid)
|
fPermsPtr, e := s.Get(fid, gid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return fp, err
|
return fp, e
|
||||||
}
|
}
|
||||||
return *fPermsPtr, nil
|
return *fPermsPtr, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,18 +81,21 @@ type MemoryForumStore struct {
|
||||||
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
||||||
acc := qgen.NewAcc()
|
acc := qgen.NewAcc()
|
||||||
f := "forums"
|
f := "forums"
|
||||||
|
set := func(s string) *sql.Stmt {
|
||||||
|
return acc.Update(f).Set(s).Where("fid=?").Prepare()
|
||||||
|
}
|
||||||
// TODO: Do a proper delete
|
// TODO: Do a proper delete
|
||||||
return &MemoryForumStore{
|
return &MemoryForumStore{
|
||||||
get: acc.Select(f).Columns("name, desc, tmpl, active, order, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Where("fid=?").Prepare(),
|
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(),
|
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(),
|
delete: set("name='',active=0"),
|
||||||
create: acc.Insert(f).Columns("name,desc,tmpl,active,preset").Fields("?,?,'',?,?").Prepare(),
|
create: acc.Insert(f).Columns("name,desc,tmpl,active,preset").Fields("?,?,'',?,?").Prepare(),
|
||||||
count: acc.Count(f).Where("name != ''").Prepare(),
|
count: acc.Count(f).Where("name != ''").Prepare(),
|
||||||
updateCache: acc.Update(f).Set("lastTopicID=?, lastReplyerID=?").Where("fid=?").Prepare(),
|
updateCache: set("lastTopicID=?,lastReplyerID=?"),
|
||||||
addTopics: acc.Update(f).Set("topicCount=topicCount+?").Where("fid=?").Prepare(),
|
addTopics: set("topicCount=topicCount+?"),
|
||||||
removeTopics: acc.Update(f).Set("topicCount=topicCount-?").Where("fid=?").Prepare(),
|
removeTopics: set("topicCount=topicCount-?"),
|
||||||
lastTopic: acc.Select("topics").Columns("tid").Where("parentID=?").Orderby("lastReplyAt DESC,createdAt DESC").Limit("1").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(),
|
updateOrder: set("order=?"),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,11 +44,13 @@ var groupStmts GroupStmts
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
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{
|
groupStmts = GroupStmts{
|
||||||
updateGroup: acc.Update(ug).Set("name=?,tag=?").Where("gid=?").Prepare(),
|
updateGroup: set("name=?,tag=?"),
|
||||||
updateGroupRank: acc.Update(ug).Set("is_admin=?,is_mod=?,is_banned=?").Where("gid=?").Prepare(),
|
updateGroupRank: set("is_admin=?,is_mod=?,is_banned=?"),
|
||||||
updateGroupPerms: acc.Update(ug).Set("permissions=?").Where("gid=?").Prepare(),
|
updateGroupPerms: set("permissions=?"),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
|
|
@ -141,22 +141,20 @@ func (s *MemoryGroupStore) GetCopy(id int) (Group, error) {
|
||||||
|
|
||||||
func (s *MemoryGroupStore) Reload(id int) error {
|
func (s *MemoryGroupStore) Reload(id int) error {
|
||||||
// TODO: Reload this data too
|
// TODO: Reload this data too
|
||||||
g, err := s.Get(id)
|
g, e := s.Get(id)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
LogError(errors.New("can't get cansee data for group #" + strconv.Itoa(id)))
|
LogError(errors.New("can't get cansee data for group #" + strconv.Itoa(id)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
canSee := g.CanSee
|
canSee := g.CanSee
|
||||||
|
|
||||||
g = &Group{ID: id, CanSee: 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)
|
e = s.get.QueryRow(id).Scan(&g.Name, &g.PermissionsText, &g.PluginPermsText, &g.IsMod, &g.IsAdmin, &g.IsBanned, &g.Tag)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
if e = s.initGroup(g); e != nil {
|
||||||
err = s.initGroup(g)
|
LogError(e)
|
||||||
if err != nil {
|
|
||||||
LogError(err)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,19 +164,19 @@ func (s *MemoryGroupStore) Reload(id int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MemoryGroupStore) initGroup(g *Group) error {
|
func (s *MemoryGroupStore) initGroup(g *Group) error {
|
||||||
err := json.Unmarshal(g.PermissionsText, &g.Perms)
|
e := json.Unmarshal(g.PermissionsText, &g.Perms)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Printf("group: %+v\n", g)
|
log.Printf("g: %+v\n", g)
|
||||||
log.Print("bad group perms: ", g.PermissionsText)
|
log.Print("bad group perms: ", g.PermissionsText)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
DebugLogf(g.Name+": %+v\n", g.Perms)
|
DebugLogf(g.Name+": %+v\n", g.Perms)
|
||||||
|
|
||||||
err = json.Unmarshal(g.PluginPermsText, &g.PluginPerms)
|
e = json.Unmarshal(g.PluginPermsText, &g.PluginPerms)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Printf("group: %+v\n", g)
|
log.Printf("g: %+v\n", g)
|
||||||
log.Print("bad group plugin perms: ", g.PluginPermsText)
|
log.Print("bad group plugin perms: ", g.PluginPermsText)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
DebugLogf(g.Name+": %+v\n", g.PluginPerms)
|
DebugLogf(g.Name+": %+v\n", g.PluginPerms)
|
||||||
|
|
||||||
|
@ -188,9 +186,9 @@ func (s *MemoryGroupStore) initGroup(g *Group) error {
|
||||||
g.IsBanned = false
|
g.IsBanned = false
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.userCount.QueryRow(g.ID).Scan(&g.UserCount)
|
e = s.userCount.QueryRow(g.ID).Scan(&g.UserCount)
|
||||||
if err != sql.ErrNoRows {
|
if e != sql.ErrNoRows {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -655,6 +655,12 @@ type PanelEditForumPage struct {
|
||||||
Active bool
|
Active bool
|
||||||
Preset string
|
Preset string
|
||||||
Groups []GroupForumPermPreset
|
Groups []GroupForumPermPreset
|
||||||
|
Actions []*ForumActionAction
|
||||||
|
}
|
||||||
|
|
||||||
|
type ForumActionAction struct {
|
||||||
|
*ForumAction
|
||||||
|
ActionName string
|
||||||
}
|
}
|
||||||
|
|
||||||
type NameLangToggle struct {
|
type NameLangToggle struct {
|
||||||
|
@ -793,11 +799,12 @@ type PanelDebugPage struct {
|
||||||
DBVersion string
|
DBVersion string
|
||||||
Uptime string
|
Uptime string
|
||||||
|
|
||||||
OpenConns int
|
DBConns int
|
||||||
DBAdapter string
|
DBAdapter string
|
||||||
|
|
||||||
Goroutines int
|
Goroutines int
|
||||||
CPUs int
|
CPUs int
|
||||||
|
HttpConns int
|
||||||
|
|
||||||
Tasks DebugPageTasks
|
Tasks DebugPageTasks
|
||||||
MemStats runtime.MemStats
|
MemStats runtime.MemStats
|
||||||
|
|
|
@ -39,11 +39,11 @@ func NewDefaultBlockStore(acc *qgen.Accumulator) (*DefaultBlockStore, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultBlockStore) IsBlockedBy(blocker, blockee int) (bool, error) {
|
func (s *DefaultBlockStore) IsBlockedBy(blocker, blockee int) (bool, error) {
|
||||||
err := s.isBlocked.QueryRow(blocker, blockee).Scan(&blocker)
|
e := s.isBlocked.QueryRow(blocker, blockee).Scan(&blocker)
|
||||||
if err == ErrNoRows {
|
if e == ErrNoRows {
|
||||||
return false, nil
|
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?
|
// 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)
|
return s.IsBlockedBy(blockers[0], blockee)
|
||||||
}
|
}
|
||||||
idList, q := inqbuild(blockers)
|
idList, q := inqbuild(blockers)
|
||||||
count, err := qgen.NewAcc().Count("users_blocks").Where("blocker IN(" + q + ") AND blockedUser=?").TotalP(idList...)
|
count, e := qgen.NewAcc().Count("users_blocks").Where("blocker IN(" + q + ") AND blockedUser=?").TotalP(idList...)
|
||||||
if err == ErrNoRows {
|
if e == ErrNoRows {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return count == 0, err
|
return count == 0, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultBlockStore) Add(blocker, blockee int) error {
|
func (s *DefaultBlockStore) Add(blocker, blockee int) error {
|
||||||
_, err := s.add.Exec(blocker, blockee)
|
_, e := s.add.Exec(blocker, blockee)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultBlockStore) Remove(blocker, blockee int) error {
|
func (s *DefaultBlockStore) Remove(blocker, blockee int) error {
|
||||||
_, err := s.remove.Exec(blocker, blockee)
|
_, e := s.remove.Exec(blocker, blockee)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultBlockStore) BlockedByOffset(blocker, offset, perPage int) (uids []int, err error) {
|
func (s *DefaultBlockStore) BlockedByOffset(blocker, offset, perPage int) (uids []int, err error) {
|
||||||
rows, err := s.blockedBy.Query(blocker, offset, perPage)
|
rows, e := s.blockedBy.Query(blocker, offset, perPage)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return nil, err
|
return nil, e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var uid int
|
var uid int
|
||||||
err := rows.Scan(&uid)
|
if e := rows.Scan(&uid); e != nil {
|
||||||
if err != nil {
|
return nil, e
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
uids = append(uids, uid)
|
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) {
|
func (s *DefaultBlockStore) BlockedByCount(blocker int) (count int) {
|
||||||
err := s.blockedByCount.QueryRow(blocker).Scan(&count)
|
e := s.blockedByCount.QueryRow(blocker).Scan(&count)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
LogError(err)
|
LogError(e)
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
@ -127,13 +126,13 @@ func NewDefaultFriendStore(acc *qgen.Accumulator) (*DefaultFriendStore, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultFriendStore) AddInvite(requester, target int) error {
|
func (s *DefaultFriendStore) AddInvite(requester, target int) error {
|
||||||
_, err := s.addInvite.Exec(requester, target)
|
_, e := s.addInvite.Exec(requester, target)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultFriendStore) Confirm(requester, target int) error {
|
func (s *DefaultFriendStore) Confirm(requester, target int) error {
|
||||||
_, err := s.confirm.Exec(requester, target)
|
_, e := s.confirm.Exec(requester, target)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultFriendStore) GetOwnSentInvites(uid int) ([]FriendInvite, error) {
|
func (s *DefaultFriendStore) GetOwnSentInvites(uid int) ([]FriendInvite, error) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TaskStmts struct {
|
type TaskStmts struct {
|
||||||
|
@ -68,18 +68,22 @@ func AddShutdownTask(task func() error) {
|
||||||
func ScheduledHalfSecondTaskCount() int {
|
func ScheduledHalfSecondTaskCount() int {
|
||||||
return len(ScheduledHalfSecondTasks)
|
return len(ScheduledHalfSecondTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduledSecondTaskCount is not concurrency safe
|
// ScheduledSecondTaskCount is not concurrency safe
|
||||||
func ScheduledSecondTaskCount() int {
|
func ScheduledSecondTaskCount() int {
|
||||||
return len(ScheduledSecondTasks)
|
return len(ScheduledSecondTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduledFifteenMinuteTaskCount is not concurrency safe
|
// ScheduledFifteenMinuteTaskCount is not concurrency safe
|
||||||
func ScheduledFifteenMinuteTaskCount() int {
|
func ScheduledFifteenMinuteTaskCount() int {
|
||||||
return len(ScheduledFifteenMinuteTasks)
|
return len(ScheduledFifteenMinuteTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduledHourTaskCount is not concurrency safe
|
// ScheduledHourTaskCount is not concurrency safe
|
||||||
func ScheduledHourTaskCount() int {
|
func ScheduledHourTaskCount() int {
|
||||||
return len(ScheduledHourTasks)
|
return len(ScheduledHourTasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShutdownTaskCount is not concurrency safe
|
// ShutdownTaskCount is not concurrency safe
|
||||||
func ShutdownTaskCount() int {
|
func ShutdownTaskCount() int {
|
||||||
return len(ShutdownTasks)
|
return len(ShutdownTasks)
|
||||||
|
@ -87,25 +91,23 @@ func ShutdownTaskCount() int {
|
||||||
|
|
||||||
// TODO: Use AddScheduledSecondTask
|
// TODO: Use AddScheduledSecondTask
|
||||||
func HandleExpiredScheduledGroups() error {
|
func HandleExpiredScheduledGroups() error {
|
||||||
rows, err := taskStmts.getExpiredScheduledGroups.Query()
|
rows, e := taskStmts.getExpiredScheduledGroups.Query()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var uid int
|
var uid int
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&uid)
|
if e := rows.Scan(&uid); e != nil {
|
||||||
if err != nil {
|
return e
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sneaky way of initialising a *User, please use the methods on the UserStore instead
|
// Sneaky way of initialising a *User, please use the methods on the UserStore instead
|
||||||
user := BlankUser()
|
user := BlankUser()
|
||||||
user.ID = uid
|
user.ID = uid
|
||||||
err = user.RevertGroupUpdate()
|
e = user.RevertGroupUpdate()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
|
@ -122,28 +124,28 @@ func HandleServerSync() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastUpdate time.Time
|
var lastUpdate time.Time
|
||||||
err := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
e := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
if lastUpdate.After(lastSync) {
|
if lastUpdate.After(lastSync) {
|
||||||
err = Forums.LoadForums()
|
e = Forums.LoadForums()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Print("Unable to reload the forums")
|
log.Print("Unable to reload the forums")
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
// TODO: Resync the groups
|
// TODO: Resync the groups
|
||||||
// TODO: Resync the permissions
|
// TODO: Resync the permissions
|
||||||
err = LoadSettings()
|
e = LoadSettings()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Print("Unable to reload the settings")
|
log.Print("Unable to reload the settings")
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = WordFilters.ReloadAll()
|
e = WordFilters.ReloadAll()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Print("Unable to reload the word filters")
|
log.Print("Unable to reload the word filters")
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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}
|
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}
|
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}
|
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_debug", "c.PanelDebugPage", dpage)
|
||||||
//t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter})
|
//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 == "" {
|
if content == "" {
|
||||||
return //log.Fatal("No content body for " + name)
|
return //log.Fatal("No content body for " + name)
|
||||||
}
|
}
|
||||||
err := writeFile("./tmpl_"+name+".go", content)
|
e := writeFile("./tmpl_"+name+".go", content)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
@ -595,9 +595,9 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
|
||||||
if tname != "" {
|
if tname != "" {
|
||||||
tname = "_" + tname
|
tname = "_" + tname
|
||||||
}
|
}
|
||||||
err := writeFile(dirPrefix+"tmpl_"+name+tname+".jgo", content)
|
e := writeFile(dirPrefix+"tmpl_"+name+tname+".jgo", content)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(e)
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
@ -737,9 +737,9 @@ func writeTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string)
|
||||||
log.Print("Writing template list")
|
log.Print("Writing template list")
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
err := writeFile(prefix+"tmpl_list.go", getTemplateList(c, wg, prefix))
|
e := writeFile(prefix+"tmpl_list.go", getTemplateList(c, wg, prefix))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(e)
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
282
common/topic.go
282
common/topic.go
|
@ -199,6 +199,7 @@ type TopicStmts struct {
|
||||||
deleteActivity *sql.Stmt
|
deleteActivity *sql.Stmt
|
||||||
edit *sql.Stmt
|
edit *sql.Stmt
|
||||||
setPoll *sql.Stmt
|
setPoll *sql.Stmt
|
||||||
|
testSetCreatedAt *sql.Stmt
|
||||||
createAction *sql.Stmt
|
createAction *sql.Stmt
|
||||||
|
|
||||||
getTopicUser *sql.Stmt // TODO: Can we get rid of this?
|
getTopicUser *sql.Stmt // TODO: Can we get rid of this?
|
||||||
|
@ -209,31 +210,35 @@ var topicStmts TopicStmts
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
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{
|
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", "?,?"),
|
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", "?,?"),
|
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(),
|
getReplies3: acc.Select("replies").Columns("rid,content,createdBy,createdAt,lastEdit,lastEditBy,likeCount,attachCount,actionType").Where(w).Orderby("rid ASC").Limit("?,?").Prepare(),
|
||||||
addReplies: acc.Update(t).Set("postCount=postCount+?, lastReplyBy=?, lastReplyAt=UTC_TIMESTAMP()").Where("tid=?").Prepare(),
|
addReplies: set("postCount=postCount+?,lastReplyBy=?,lastReplyAt=UTC_TIMESTAMP()"),
|
||||||
updateLastReply: acc.Update(t).Set("lastReplyID=?").Where("lastReplyID<? AND tid=?").Prepare(),
|
updateLastReply: acc.Update(t).Set("lastReplyID=?").Where("lastReplyID<? AND tid=?").Prepare(),
|
||||||
lock: acc.Update(t).Set("is_closed=1").Where("tid=?").Prepare(),
|
lock: set("is_closed=1"),
|
||||||
unlock: acc.Update(t).Set("is_closed=0").Where("tid=?").Prepare(),
|
unlock: set("is_closed=0"),
|
||||||
moveTo: acc.Update(t).Set("parentID=?").Where("tid=?").Prepare(),
|
moveTo: set("parentID=?"),
|
||||||
stick: acc.Update(t).Set("sticky=1").Where("tid=?").Prepare(),
|
stick: set("sticky=1"),
|
||||||
unstick: acc.Update(t).Set("sticky=0").Where("tid=?").Prepare(),
|
unstick: set("sticky=0"),
|
||||||
hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy=? and targetItem=? and targetType='topics'").Prepare(),
|
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(),
|
createLike: acc.Insert("likes").Columns("weight,targetItem,targetType,sentBy,createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||||
addLikesToTopic: acc.Update(t).Set("likeCount=likeCount+?").Where("tid=?").Prepare(),
|
addLikesToTopic: set("likeCount=likeCount+?"),
|
||||||
delete: acc.Delete(t).Where("tid=?").Prepare(),
|
delete: acc.Delete(t).Where(w).Prepare(),
|
||||||
deleteReplies: acc.Delete("replies").Where("tid=?").Prepare(),
|
deleteReplies: acc.Delete("replies").Where(w).Prepare(),
|
||||||
deleteLikesForTopic: acc.Delete("likes").Where("targetItem=? AND targetType='topics'").Prepare(),
|
deleteLikesForTopic: acc.Delete("likes").Where("targetItem=? AND targetType='topics'").Prepare(),
|
||||||
deleteActivity: acc.Delete("activity_stream").Where("elementID=? AND elementType='topic'").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(),
|
setPoll: acc.Update(t).Set("poll=?").Where("tid=? AND poll=0").Prepare(),
|
||||||
|
testSetCreatedAt: set("createdAt=?"),
|
||||||
createAction: acc.Insert("replies").Columns("tid,actionType,ip,createdBy,createdAt,lastUpdated,content,parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
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=?", "", ""),
|
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()
|
return acc.FirstError()
|
||||||
|
@ -250,52 +255,58 @@ func (t *Topic) cacheRemove() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func (t *Topic) AddReply(rid, uid int) (err error) {
|
func (t *Topic) AddReply(rid, uid int) (e error) {
|
||||||
_, err = topicStmts.addReplies.Exec(1, uid, t.ID)
|
_, e = topicStmts.addReplies.Exec(1, uid, t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, err = topicStmts.updateLastReply.Exec(rid, rid, t.ID)
|
_, e = topicStmts.updateLastReply.Exec(rid, rid, t.ID)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topic) Lock() (err error) {
|
func (t *Topic) Lock() (e error) {
|
||||||
_, err = topicStmts.lock.Exec(t.ID)
|
_, e = topicStmts.lock.Exec(t.ID)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topic) Unlock() (err error) {
|
func (t *Topic) Unlock() (e error) {
|
||||||
_, err = topicStmts.unlock.Exec(t.ID)
|
_, e = topicStmts.unlock.Exec(t.ID)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topic) MoveTo(destForum int) (err error) {
|
func (t *Topic) MoveTo(destForum int) (e error) {
|
||||||
_, err = topicStmts.moveTo.Exec(destForum, t.ID)
|
_, e = topicStmts.moveTo.Exec(destForum, t.ID)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = Attachments.MoveTo(destForum, t.ID, "topics")
|
e = Attachments.MoveTo(destForum, t.ID, "topics")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return Attachments.MoveToByExtra(destForum, "replies", strconv.Itoa(t.ID))
|
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) TestSetCreatedAt(s time.Time) (e error) {
|
||||||
func (t *Topic) Stick() (err error) {
|
_, e = topicStmts.testSetCreatedAt.Exec(s, t.ID)
|
||||||
_, err = topicStmts.stick.Exec(t.ID)
|
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topic) Unstick() (err error) {
|
// 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.
|
||||||
_, err = topicStmts.unstick.Exec(t.ID)
|
func (t *Topic) Stick() (e error) {
|
||||||
|
_, e = topicStmts.stick.Exec(t.ID)
|
||||||
t.cacheRemove()
|
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
|
// TODO: Test this
|
||||||
|
@ -323,49 +334,46 @@ func (t *Topic) Like(score, uid int) (err error) {
|
||||||
|
|
||||||
// TODO: Use a transaction
|
// TODO: Use a transaction
|
||||||
func (t *Topic) Unlike(uid int) error {
|
func (t *Topic) Unlike(uid int) error {
|
||||||
err := Likes.Delete(t.ID, "topics")
|
e := Likes.Delete(t.ID, "topics")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, err = topicStmts.addLikesToTopic.Exec(-1, t.ID)
|
_, e = topicStmts.addLikesToTopic.Exec(-1, t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, err = userStmts.decLiked.Exec(1, uid)
|
_, e = userStmts.decLiked.Exec(1, uid)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLikedTopicReplies(tid int) error {
|
func handleLikedTopicReplies(tid int) error {
|
||||||
rows, err := userStmts.getLikedRepliesOfTopic.Query(tid)
|
rows, e := userStmts.getLikedRepliesOfTopic.Query(tid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var rid int
|
var rid int
|
||||||
err := rows.Scan(&rid)
|
if e := rows.Scan(&rid); e != nil {
|
||||||
if err != nil {
|
return e
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
_, err = replyStmts.deleteLikesForReply.Exec(rid)
|
_, e = replyStmts.deleteLikesForReply.Exec(rid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = Activity.DeleteByParams("like", rid, "post")
|
e = Activity.DeleteByParams("like", rid, "post")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleTopicAttachments(tid int) error {
|
func handleTopicAttachments(tid int) error {
|
||||||
err := handleAttachments(userStmts.getAttachmentsOfTopic, tid)
|
e := handleAttachments(userStmts.getAttachmentsOfTopic, tid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return handleAttachments(userStmts.getAttachmentsOfTopic2, tid)
|
return handleAttachments(userStmts.getAttachmentsOfTopic2, tid)
|
||||||
}
|
}
|
||||||
|
@ -375,120 +383,113 @@ func handleReplyAttachments(rid int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAttachments(stmt *sql.Stmt, id int) error {
|
func handleAttachments(stmt *sql.Stmt, id int) error {
|
||||||
rows, err := stmt.Query(id)
|
rows, e := stmt.Query(id)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var aid int
|
var aid int
|
||||||
err := rows.Scan(&aid)
|
if e := rows.Scan(&aid); e != nil {
|
||||||
if err != nil {
|
return e
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
a, err := Attachments.FGet(aid)
|
a, e := Attachments.FGet(aid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = deleteAttachment(a)
|
e = deleteAttachment(a)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if e != nil && e != sql.ErrNoRows {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Only load a row per createdBy, maybe with group by?
|
// TODO: Only load a row per createdBy, maybe with group by?
|
||||||
func handleTopicReplies(umap map[int]struct{}, uid, tid int) error {
|
func handleTopicReplies(umap map[int]struct{}, uid, tid int) error {
|
||||||
rows, err := userStmts.getRepliesOfTopic.Query(uid, tid)
|
rows, e := userStmts.getRepliesOfTopic.Query(uid, tid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var createdBy int
|
var createdBy int
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&createdBy)
|
if e := rows.Scan(&createdBy); e != nil {
|
||||||
if err != nil {
|
return e
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
umap[createdBy] = struct{}{}
|
umap[createdBy] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use a transaction here
|
// TODO: Use a transaction here
|
||||||
func (t *Topic) Delete() error {
|
func (t *Topic) Delete() error {
|
||||||
/*creator, err := Users.Get(t.CreatedBy)
|
/*creator, e := Users.Get(t.CreatedBy)
|
||||||
if err == nil {
|
if e == nil {
|
||||||
err = creator.DecreasePostStats(WordCount(t.Content), true)
|
e = creator.DecreasePostStats(WordCount(t.Content), true)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
} else if err != ErrNoRows {
|
} else if e != ErrNoRows {
|
||||||
return err
|
return e
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// TODO: Clear reply cache too
|
// TODO: Clear reply cache too
|
||||||
_, err := topicStmts.delete.Exec(t.ID)
|
_, e := topicStmts.delete.Exec(t.ID)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = Forums.RemoveTopic(t.ParentID)
|
e = Forums.RemoveTopic(t.ParentID)
|
||||||
if err != nil && err != ErrNoRows {
|
if e != nil && e != ErrNoRows {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, err = topicStmts.deleteLikesForTopic.Exec(t.ID)
|
_, e = topicStmts.deleteLikesForTopic.Exec(t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.PostCount > 1 {
|
if t.PostCount > 1 {
|
||||||
err = handleLikedTopicReplies(t.ID)
|
if e = handleLikedTopicReplies(t.ID); e != nil {
|
||||||
if err != nil {
|
return e
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
umap := make(map[int]struct{})
|
umap := make(map[int]struct{})
|
||||||
err = handleTopicReplies(umap, t.CreatedBy, t.ID)
|
e = handleTopicReplies(umap, t.CreatedBy, t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, err = topicStmts.deleteReplies.Exec(t.ID)
|
_, e = topicStmts.deleteReplies.Exec(t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
for uid := range umap {
|
for uid := range umap {
|
||||||
err = (&User{ID: uid}).RecalcPostStats()
|
e = (&User{ID: uid}).RecalcPostStats()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
//log.Printf("err: %+v\n", err)
|
//log.Printf("e: %+v\n", e)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = (&User{ID: t.CreatedBy}).RecalcPostStats()
|
e = (&User{ID: t.CreatedBy}).RecalcPostStats()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = handleTopicAttachments(t.ID)
|
e = handleTopicAttachments(t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = Subscriptions.DeleteResource(t.ID, "topic")
|
e = Subscriptions.DeleteResource(t.ID, "topic")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, err = topicStmts.deleteActivity.Exec(t.ID)
|
_, e = topicStmts.deleteActivity.Exec(t.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
if t.Poll > 0 {
|
if t.Poll > 0 {
|
||||||
err = (&Poll{ID: t.Poll}).Delete()
|
e = (&Poll{ID: t.Poll}).Delete()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -513,9 +514,9 @@ func (t *Topic) Update(name, content string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topic) SetPoll(pollID int) 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()
|
t.cacheRemove()
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Have this go through the ReplyStore?
|
// TODO: Have this go through the ReplyStore?
|
||||||
|
@ -543,22 +544,19 @@ func (t *Topic) CreateActionReply(action, ip string, uid int) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRidsForTopic(tid, offset int) (rids []int, err error) {
|
func GetRidsForTopic(tid, offset int) (rids []int, e error) {
|
||||||
rows, err := topicStmts.getRids.Query(tid, offset, Config.ItemsPerPage)
|
rows, e := topicStmts.getRids.Query(tid, offset, Config.ItemsPerPage)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return nil, err
|
return nil, e
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var rid int
|
var rid int
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&rid)
|
if e := rows.Scan(&rid); e != nil {
|
||||||
if err != nil {
|
return nil, e
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
rids = append(rids, rid)
|
rids = append(rids, rid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rids, rows.Err()
|
return rids, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,9 @@ type weakpassHolder struct {
|
||||||
|
|
||||||
func InitWeakPasswords() error {
|
func InitWeakPasswords() error {
|
||||||
var weakpass weakpassHolder
|
var weakpass weakpassHolder
|
||||||
err := unmarshalJsonFile("./config/weakpass_default.json", &weakpass)
|
e := unmarshalJsonFile("./config/weakpass_default.json", &weakpass)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
wcon := make(map[string]struct{})
|
wcon := make(map[string]struct{})
|
||||||
|
@ -41,9 +41,9 @@ func InitWeakPasswords() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
weakpass = weakpassHolder{}
|
weakpass = weakpassHolder{}
|
||||||
err = unmarshalJsonFileIgnore404("./config/weakpass.json", &weakpass)
|
e = unmarshalJsonFileIgnore404("./config/weakpass.json", &weakpass)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range weakpass.Contains {
|
for _, item := range weakpass.Contains {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Gosora WebSocket Subsystem
|
* Gosora WebSocket Subsystem
|
||||||
* Copyright Azareal 2017 - 2020
|
* Copyright Azareal 2017 - 2021
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package common
|
package common
|
||||||
|
@ -149,12 +149,12 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||||
if wsUser.User.ID == 0 {
|
if wsUser.User.ID == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, tid, err := ParseSEOURL(page)
|
_, tid, e := ParseSEOURL(page)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
topic, err := Topics.Get(tid)
|
topic, e := Topics.Get(tid)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !Forums.Exists(topic.ParentID) {
|
if !Forums.Exists(topic.ParentID) {
|
||||||
|
@ -169,10 +169,10 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||||
return
|
return
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
fperms, err := FPStore.Get(topic.ParentID, usercpy.Group)
|
fperms, e := FPStore.Get(topic.ParentID, usercpy.Group)
|
||||||
if err == ErrNoRows {
|
if e == ErrNoRows {
|
||||||
fperms = BlankForumPerms()
|
fperms = BlankForumPerms()
|
||||||
} else if err != nil {
|
} else if e != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cascadeForumPerms(fperms, usercpy)
|
cascadeForumPerms(fperms, usercpy)
|
||||||
|
@ -202,9 +202,9 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := wsUser.SetPageForSocket(conn, page)
|
e := wsUser.SetPageForSocket(conn, page)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
LogError(err)
|
LogError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,8 +248,8 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wsUser.FinalizePage(page, func() {
|
wsUser.FinalizePage(page, func() {
|
||||||
_, tid, err := ParseSEOURL(page)
|
_, tid, e := ParseSEOURL(page)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
topicMutex.Lock()
|
topicMutex.Lock()
|
||||||
|
@ -258,8 +258,7 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, ok = topic[wsUser]
|
if _, ok = topic[wsUser]; !ok {
|
||||||
if !ok {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete(topic, wsUser)
|
delete(topic, wsUser)
|
||||||
|
@ -272,9 +271,9 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
|
||||||
delete(adminStatsWatchers, conn)
|
delete(adminStatsWatchers, conn)
|
||||||
adminStatsMutex.Unlock()
|
adminStatsMutex.Unlock()
|
||||||
}
|
}
|
||||||
err := wsUser.SetPageForSocket(conn, "")
|
e := wsUser.SetPageForSocket(conn, "")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
LogError(err)
|
LogError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,9 +289,7 @@ var adminStatsMutex sync.RWMutex
|
||||||
func adminStatsTicker() {
|
func adminStatsTicker() {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
lastUonline := -1
|
lastUonline, lastGonline, lastTotonline := -1, -1, -1
|
||||||
lastGonline := -1
|
|
||||||
lastTotonline := -1
|
|
||||||
lastCPUPerc := -1
|
lastCPUPerc := -1
|
||||||
var lastAvailableRAM int64 = -1
|
var lastAvailableRAM int64 = -1
|
||||||
var noStatUpdates, noRAMUpdates bool
|
var noStatUpdates, noRAMUpdates bool
|
||||||
|
|
|
@ -73,10 +73,10 @@ func (w *Widget) Delete() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Widget) Copy() (owidget *Widget) {
|
func (w *Widget) Copy() (ow *Widget) {
|
||||||
owidget = &Widget{}
|
ow = &Widget{}
|
||||||
*owidget = *w
|
*ow = *w
|
||||||
return owidget
|
return ow
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
|
|
|
@ -28,8 +28,8 @@ func (u *WSUser) Ping() error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
socket.conn.SetWriteDeadline(time.Now().Add(time.Minute))
|
socket.conn.SetWriteDeadline(time.Now().Add(time.Minute))
|
||||||
err := socket.conn.WriteMessage(websocket.PingMessage, nil)
|
e := socket.conn.WriteMessage(websocket.PingMessage, nil)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
socket.conn.Close()
|
socket.conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ func (u *WSUser) WriteAll(msg string) error {
|
||||||
if socket == nil {
|
if socket == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
w, err := socket.conn.NextWriter(websocket.TextMessage)
|
w, e := socket.conn.NextWriter(websocket.TextMessage)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
_, _ = w.Write(msgbytes)
|
_, _ = w.Write(msgbytes)
|
||||||
w.Close()
|
w.Close()
|
||||||
|
@ -66,8 +66,8 @@ func (u *WSUser) WriteToPageBytes(msg []byte, page string) error {
|
||||||
if socket.Page != page {
|
if socket.Page != page {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
w, err := socket.conn.NextWriter(websocket.TextMessage)
|
w, e := socket.conn.NextWriter(websocket.TextMessage)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
continue // Skip dead sockets, a dedicated goroutine handles those
|
continue // Skip dead sockets, a dedicated goroutine handles those
|
||||||
}
|
}
|
||||||
_, _ = w.Write(msg)
|
_, _ = w.Write(msg)
|
||||||
|
@ -90,8 +90,8 @@ func (u *WSUser) WriteToPageBytesMulti(msgs [][]byte, page string) error {
|
||||||
if socket.Page != page {
|
if socket.Page != page {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
w, err := socket.conn.NextWriter(websocket.TextMessage)
|
w, e := socket.conn.NextWriter(websocket.TextMessage)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
continue // Skip dead sockets, a dedicated goroutine handles those
|
continue // Skip dead sockets, a dedicated goroutine handles those
|
||||||
}
|
}
|
||||||
for _, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
|
@ -184,7 +184,7 @@ func (u *WSUser) InPage(page string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *WSUser) FinalizePage(page string, handle func()) {
|
func (u *WSUser) FinalizePage(page string, h func()) {
|
||||||
u.Lock()
|
u.Lock()
|
||||||
defer u.Unlock()
|
defer u.Unlock()
|
||||||
for _, socket := range u.Sockets {
|
for _, socket := range u.Sockets {
|
||||||
|
@ -195,5 +195,5 @@ func (u *WSUser) FinalizePage(page string, handle func()) {
|
||||||
return
|
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 {
|
func createAdmin() error {
|
||||||
fmt.Println("Creating the admin user")
|
fmt.Println("Creating the admin user")
|
||||||
hashedPassword, salt, err := BcryptGeneratePassword("password")
|
hashedPassword, salt, e := BcryptGeneratePassword("password")
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the admin user query
|
// 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(),'',''")
|
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 err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the admin user query
|
// Run the admin user query
|
||||||
_, err = adminUserStmt.Exec(hashedPassword, salt)
|
_, e = adminUserStmt.Exec(hashedPassword, salt)
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,9 @@
|
||||||
"megapost_min_words":"Mega Post Minimum Words",
|
"megapost_min_words":"Mega Post Minimum Words",
|
||||||
"meta_desc":"Meta Description",
|
"meta_desc":"Meta Description",
|
||||||
"rapid_loading":"Rapid Loaded?",
|
"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": {
|
"PermPresets": {
|
||||||
|
@ -479,14 +481,14 @@
|
||||||
"panel_rank_guests":"Guests",
|
"panel_rank_guests":"Guests",
|
||||||
"panel_rank_members":"Members",
|
"panel_rank_members":"Members",
|
||||||
|
|
||||||
"panel_preset_everyone":"Everyone",
|
"panel.preset_everyone":"Everyone",
|
||||||
"panel_preset_announcements":"Announcements",
|
"panel.preset_announcements":"Announcements",
|
||||||
"panel_preset_member_only":"Member Only",
|
"panel.preset_member_only":"Member Only",
|
||||||
"panel_preset_staff_only":"Staff Only",
|
"panel.preset_staff_only":"Staff Only",
|
||||||
"panel_preset_admin_only":"Admin Only",
|
"panel.preset_admin_only":"Admin Only",
|
||||||
"panel_preset_archive":"Archive",
|
"panel.preset_archive":"Archive",
|
||||||
"panel_preset_custom":"Custom",
|
"panel.preset_custom":"Custom",
|
||||||
"panel_preset_public":"Public",
|
"panel.preset_public":"Public",
|
||||||
"panel_active_hidden":"Hidden",
|
"panel_active_hidden":"Hidden",
|
||||||
|
|
||||||
"panel_perms_no_access":"No Access",
|
"panel_perms_no_access":"No Access",
|
||||||
|
@ -693,7 +695,7 @@
|
||||||
"forum_start_one":"Start one?",
|
"forum_start_one":"Start one?",
|
||||||
|
|
||||||
"forums_head":"Forums",
|
"forums_head":"Forums",
|
||||||
"forums_no_description":"No description",
|
"forums_no_desc":"No description",
|
||||||
"forums_none":"None",
|
"forums_none":"None",
|
||||||
"forums_no_forums":"You don't have access to any forums.",
|
"forums_no_forums":"You don't have access to any forums.",
|
||||||
|
|
||||||
|
@ -944,26 +946,40 @@
|
||||||
"panel.forums_create_head":"Add Forum",
|
"panel.forums_create_head":"Add Forum",
|
||||||
"panel.forums_create_name_label":"Name",
|
"panel.forums_create_name_label":"Name",
|
||||||
"panel.forums_create_name":"Super Secret Forum",
|
"panel.forums_create_name":"Super Secret Forum",
|
||||||
"panel.forums_create_description_label":"Description",
|
"panel.forums_create_desc_label":"Description",
|
||||||
"panel.forums_create_description":"Where all the super secret stuff happens",
|
"panel.forums_create_desc":"Where all the super secret stuff happens",
|
||||||
"panel.forums_active_label":"Active",
|
"panel.forums_active_label":"Active",
|
||||||
"panel.forums_preset_label":"Preset",
|
"panel.forums_preset_label":"Preset",
|
||||||
"panel.forums_create_button":"Add Forum",
|
"panel.forums_create_button":"Add Forum",
|
||||||
"panel.forums_update_order_button":"Update Order",
|
"panel.forums_update_order_button":"Update Order",
|
||||||
"panel.forums_order_updated":"The forums have been successfully updated",
|
"panel.forums_order_updated":"The forums have been successfully updated",
|
||||||
|
|
||||||
"panel_forum_head_suffix":" Forum",
|
"panel.forum_head_suffix":" Forum",
|
||||||
"panel_forum_name":"Name",
|
"panel.forum_name":"Name",
|
||||||
"panel_forum_name_placeholder":"General Forum",
|
"panel.forum_name_placeholder":"General Forum",
|
||||||
"panel_forum_description":"Description",
|
"panel.forum_desc":"Description",
|
||||||
"panel_forum_description_placeholder":"Where the general stuff happens",
|
"panel.forum_desc_placeholder":"Where the general stuff happens",
|
||||||
"panel_forum_active":"Active",
|
"panel.forum_active":"Active",
|
||||||
"panel_forum_preset":"Preset",
|
"panel.forum_preset":"Preset",
|
||||||
"panel_forum_update_button":"Update Forum",
|
"panel.forum_update_button":"Update Forum",
|
||||||
"panel_forum_permissions_head":"Forum Permissions",
|
"panel.forum_permissions_head":"Forum Permissions",
|
||||||
"panel_forum_edit_button":"Edit",
|
"panel.forum_edit_button":"Edit",
|
||||||
"panel_forum_short_update_button":"Update",
|
"panel.forum_short_update_button":"Update",
|
||||||
"panel_forum_full_edit_button":"Full Edit",
|
"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_forum_delete_are_you_sure":"Are you sure you want to delete the '%s' forum?",
|
||||||
|
|
||||||
"panel_groups_head":"Groups",
|
"panel_groups_head":"Groups",
|
||||||
|
@ -1244,6 +1260,7 @@
|
||||||
|
|
||||||
"panel_debug_goroutine_count_label":"Goroutines",
|
"panel_debug_goroutine_count_label":"Goroutines",
|
||||||
"panel_debug_cpu_count_label":"CPUs",
|
"panel_debug_cpu_count_label":"CPUs",
|
||||||
|
"panel_debug_http_conns_label":"HTTP Conns",
|
||||||
|
|
||||||
"panel_debug_tasks":"Tasks",
|
"panel_debug_tasks":"Tasks",
|
||||||
"panel_debug_tasks_half_second":"Half Second",
|
"panel_debug_tasks_half_second":"Half Second",
|
||||||
|
|
12
main.go
12
main.go
|
@ -143,10 +143,10 @@ func storeInit() (e error) {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return ws(e)
|
return ws(e)
|
||||||
}
|
}
|
||||||
/*c.ForumActionStore, e = c.NewDefaultForumActionStore(acc)
|
c.ForumActionStore, e = c.NewDefaultForumActionStore(acc)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return ws(e)
|
return ws(e)
|
||||||
}*/
|
}
|
||||||
c.Convos, e = c.NewDefaultConversationStore(acc)
|
c.Convos, e = c.NewDefaultConversationStore(acc)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return ws(e)
|
return ws(e)
|
||||||
|
@ -549,8 +549,7 @@ func main() {
|
||||||
|
|
||||||
// Resource Management Goroutine
|
// Resource Management Goroutine
|
||||||
go func() {
|
go func() {
|
||||||
uc := c.Users.GetCache()
|
uc, tc := c.Users.GetCache(), c.Topics.GetCache()
|
||||||
tc := c.Topics.GetCache()
|
|
||||||
if uc == nil && tc == nil {
|
if uc == nil && tc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -630,7 +629,7 @@ func main() {
|
||||||
|
|
||||||
func startServer() {
|
func startServer() {
|
||||||
// We might not need the timeouts, if we're behind a reverse-proxy like Nginx
|
// 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
|
rtime := c.Config.ReadTimeout
|
||||||
if rtime == 0 {
|
if rtime == 0 {
|
||||||
rtime = 8
|
rtime = 8
|
||||||
|
@ -651,7 +650,8 @@ func startServer() {
|
||||||
}
|
}
|
||||||
return &http.Server{
|
return &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: handler,
|
Handler: h,
|
||||||
|
ConnState: c.ConnWatch.StateChange,
|
||||||
|
|
||||||
ReadTimeout: time.Duration(rtime) * time.Second,
|
ReadTimeout: time.Duration(rtime) * time.Second,
|
||||||
WriteTimeout: time.Duration(wtime) * 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) {
|
func testReplyStore(t *testing.T, newID int, ip string) {
|
||||||
ex, exf := exp(t), expf(t)
|
ex, exf := exp(t), expf(t)
|
||||||
replyTest2 := func(r *c.Reply, err error, rid, parentID, createdBy int, content, ip string) {
|
replyTest2 := func(r *c.Reply, e error, rid, parentID, createdBy int, content, ip string) {
|
||||||
expectNilErr(t, err)
|
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.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.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)
|
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) {
|
replyTest := func(rid, parentID, createdBy int, content, ip string) {
|
||||||
r, err := c.Rstore.Get(rid)
|
r, e := c.Rstore.Get(rid)
|
||||||
replyTest2(r, err, rid, parentID, createdBy, content, ip)
|
replyTest2(r, e, rid, parentID, createdBy, content, ip)
|
||||||
r, err = c.Rstore.GetCache().Get(rid)
|
r, e = c.Rstore.GetCache().Get(rid)
|
||||||
replyTest2(r, err, rid, parentID, createdBy, content, ip)
|
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:
|
// ! 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)
|
//_, 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))
|
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) {
|
func TestTopicList(t *testing.T) {
|
||||||
ex, exf := exp(t), expf(t)
|
ex, exf := exp(t), expf(t)
|
||||||
fid, err := c.Forums.Create("Test Forum", "Desc for test forum", true, "")
|
fid, err := c.Forums.Create("Test Forum", "Desc for test forum", true, "")
|
||||||
|
|
|
@ -53,6 +53,7 @@ func init() {
|
||||||
addPatch(33, patch33)
|
addPatch(33, patch33)
|
||||||
addPatch(34, patch34)
|
addPatch(34, patch34)
|
||||||
addPatch(35, patch35)
|
addPatch(35, patch35)
|
||||||
|
addPatch(36, patch36)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bcol(col string, val bool) qgen.DBTableColumn {
|
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 {
|
func patch1(scanner *bufio.Scanner) error {
|
||||||
routes := map[string]string{
|
return renameRoutes(map[string]string{
|
||||||
"routeAccountEditCriticalSubmit": "routes.AccountEditCriticalSubmit",
|
"routeAccountEditCriticalSubmit": "routes.AccountEditCriticalSubmit",
|
||||||
"routeAccountEditAvatar": "routes.AccountEditAvatar",
|
"routeAccountEditAvatar": "routes.AccountEditAvatar",
|
||||||
"routeAccountEditAvatarSubmit": "routes.AccountEditAvatarSubmit",
|
"routeAccountEditAvatarSubmit": "routes.AccountEditAvatarSubmit",
|
||||||
"routeAccountEditUsername": "routes.AccountEditUsername",
|
"routeAccountEditUsername": "routes.AccountEditUsername",
|
||||||
"routeAccountEditUsernameSubmit": "routes.AccountEditUsernameSubmit",
|
"routeAccountEditUsernameSubmit": "routes.AccountEditUsernameSubmit",
|
||||||
}
|
})
|
||||||
return renameRoutes(routes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func patch2(scanner *bufio.Scanner) error {
|
func patch2(scanner *bufio.Scanner) error {
|
||||||
routes := map[string]string{
|
return renameRoutes(map[string]string{
|
||||||
"routeLogout": "routes.AccountLogout",
|
"routeLogout": "routes.AccountLogout",
|
||||||
"routeShowAttachment": "routes.ShowAttachment",
|
"routeShowAttachment": "routes.ShowAttachment",
|
||||||
"routeChangeTheme": "routes.ChangeTheme",
|
"routeChangeTheme": "routes.ChangeTheme",
|
||||||
|
@ -189,8 +189,7 @@ func patch2(scanner *bufio.Scanner) error {
|
||||||
"routeDynamic": "routes.DynamicRoute",
|
"routeDynamic": "routes.DynamicRoute",
|
||||||
"routeUploads": "routes.UploadedFile",
|
"routeUploads": "routes.UploadedFile",
|
||||||
"BadRoute": "routes.BadRoute",
|
"BadRoute": "routes.BadRoute",
|
||||||
}
|
})
|
||||||
return renameRoutes(routes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func patch3(scanner *bufio.Scanner) error {
|
func patch3(scanner *bufio.Scanner) error {
|
||||||
|
@ -248,14 +247,14 @@ func patch4(scanner *bufio.Scanner) error {
|
||||||
"routePanelForumsEditPermsAdvanceSubmit": "panel.ForumsEditPermsAdvanceSubmit",
|
"routePanelForumsEditPermsAdvanceSubmit": "panel.ForumsEditPermsAdvanceSubmit",
|
||||||
"routePanelBackups": "panel.Backups",
|
"routePanelBackups": "panel.Backups",
|
||||||
}
|
}
|
||||||
err := renameRoutes(routes)
|
e := renameRoutes(routes)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
err = execStmt(qgen.Builder.SimpleDelete("settings", "name='url_tags'"))
|
e = execStmt(qgen.Builder.SimpleDelete("settings", "name='url_tags'"))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
return createTable("pages", "utf8mb4", "utf8mb4_general_ci",
|
return createTable("pages", "utf8mb4", "utf8mb4_general_ci",
|
||||||
|
@ -281,14 +280,14 @@ func patch5(scanner *bufio.Scanner) error {
|
||||||
"routes.AccountEditCritical": "routes.AccountEditPassword",
|
"routes.AccountEditCritical": "routes.AccountEditPassword",
|
||||||
"routes.AccountEditCriticalSubmit": "routes.AccountEditPasswordSubmit",
|
"routes.AccountEditCriticalSubmit": "routes.AccountEditPasswordSubmit",
|
||||||
}
|
}
|
||||||
err := renameRoutes(routes)
|
e := renameRoutes(routes)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
err = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'"))
|
e = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'"))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
return createTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci",
|
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 {
|
for key, value := range routes {
|
||||||
err := replaceTextWhere(key, value)
|
e := replaceTextWhere(key, value)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,9 +369,9 @@ func patch8(scanner *bufio.Scanner) error {
|
||||||
"routePanelThemesMenuItemOrderSubmit": "panel.ThemesMenuItemOrderSubmit",
|
"routePanelThemesMenuItemOrderSubmit": "panel.ThemesMenuItemOrderSubmit",
|
||||||
"routePanelDashboard": "panel.Dashboard",
|
"routePanelDashboard": "panel.Dashboard",
|
||||||
}
|
}
|
||||||
err := renameRoutes(routes)
|
e := renameRoutes(routes)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
return createTable("updates", "", "",
|
return createTable("updates", "", "",
|
||||||
|
@ -812,36 +811,36 @@ func patch29(scanner *bufio.Scanner) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func patch30(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))
|
e := execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"registeredFor", "int", 0, false, false, "0"}, nil))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", ""))
|
return execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
func patch31(scanner *bufio.Scanner) (e error) {
|
func patch31(scanner *bufio.Scanner) (e error) {
|
||||||
addKey := func(tbl, col string, tk qgen.DBTableKey) error {
|
addKey := func(tbl, col string, tk qgen.DBTableKey) error {
|
||||||
/*err := execStmt(qgen.Builder.RemoveIndex(tbl, col))
|
/*e := execStmt(qgen.Builder.RemoveIndex(tbl, col))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}*/
|
}*/
|
||||||
return execStmt(qgen.Builder.AddKey(tbl, col, tk))
|
return execStmt(qgen.Builder.AddKey(tbl, col, tk))
|
||||||
}
|
}
|
||||||
err := addKey("topics", "title", tK{"title", "fulltext", "", false})
|
e = addKey("topics", "title", tK{"title", "fulltext", "", false})
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
err = addKey("topics", "content", tK{"content", "fulltext", "", false})
|
e = addKey("topics", "content", tK{"content", "fulltext", "", false})
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return addKey("replies", "content", tK{"content", "fulltext", "", false})
|
return addKey("replies", "content", tK{"content", "fulltext", "", false})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTable(tbl, charset, collation string, cols []tC, keys []tK) error {
|
func createTable(tbl, charset, collation string, cols []tC, keys []tK) error {
|
||||||
err := execStmt(qgen.Builder.DropTable(tbl))
|
e := execStmt(qgen.Builder.DropTable(tbl))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return execStmt(qgen.Builder.CreateTable(tbl, charset, collation, cols, keys))
|
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 {
|
func patch35(scanner *bufio.Scanner) error {
|
||||||
err := execStmt(qgen.Builder.AddColumn("topics", tC{"weekEvenViews", "int", 0, false, false, "0"}, nil))
|
e := execStmt(qgen.Builder.AddColumn("topics", tC{"weekEvenViews", "int", 0, false, false, "0"}, nil))
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return err
|
return e
|
||||||
}
|
}
|
||||||
return execStmt(qgen.Builder.AddColumn("topics", tC{"weekOddViews", "int", 0, false, false, "0"}, nil))
|
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
|
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 {
|
/*func (b *accDeleteBuilder) Prepare() *sql.Stmt {
|
||||||
return b.build.SimpleDelete(b.table, b.where)
|
return b.build.SimpleDelete(b.table, b.where)
|
||||||
}*/
|
}*/
|
||||||
|
@ -87,6 +92,11 @@ func (b *accUpdateBuilder) DateOlderThan(col string, quantity int, unit string)
|
||||||
return b
|
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 {
|
func (b *accUpdateBuilder) WhereQ(sel *selectPrebuilder) *accUpdateBuilder {
|
||||||
b.up.whereSubQuery = sel
|
b.up.whereSubQuery = sel
|
||||||
return b
|
return b
|
||||||
|
@ -98,14 +108,24 @@ func (b *accUpdateBuilder) Prepare() *sql.Stmt {
|
||||||
}
|
}
|
||||||
return b.build.prepare(b.build.adapter.SimpleUpdate(b.up))
|
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) {
|
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 {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
//fmt.Println("query:", query)
|
//fmt.Println("q:", q)
|
||||||
return b.build.exec(query, args...)
|
return b.build.exec(q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccBuilder interface {
|
||||||
|
Prepare() *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccSelectBuilder struct {
|
type AccSelectBuilder struct {
|
||||||
|
@ -147,8 +167,8 @@ func (b *AccSelectBuilder) In(col string, inList []int) *AccSelectBuilder {
|
||||||
|
|
||||||
// TODO: Optimise this
|
// TODO: Optimise this
|
||||||
where := col + " IN("
|
where := col + " IN("
|
||||||
for _, item := range inList {
|
for _, it := range inList {
|
||||||
where += strconv.Itoa(item) + ","
|
where += strconv.Itoa(it) + ","
|
||||||
}
|
}
|
||||||
where = where[:len(where)-1] + ")"
|
where = where[:len(where)-1] + ")"
|
||||||
if b.where != "" {
|
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)
|
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 {
|
func (b *AccSelectBuilder) ComplexPrepare() *sql.Stmt {
|
||||||
selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
|
selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
|
||||||
return b.build.prepare(b.build.GetAdapter().ComplexSelect(selectBuilder))
|
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"),
|
Action("panel.ForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extraData"),
|
||||||
View("panel.ForumsEditPermsAdvance", "/panel/forums/edit/perms/", "extraData"),
|
View("panel.ForumsEditPermsAdvance", "/panel/forums/edit/perms/", "extraData"),
|
||||||
Action("panel.ForumsEditPermsAdvanceSubmit", "/panel/forums/edit/perms/adv/submit/", "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.Settings", "/panel/settings/"),
|
||||||
View("panel.SettingEdit", "/panel/settings/edit/", "extraData"),
|
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()
|
goVersion := runtime.Version()
|
||||||
dbVersion := qgen.Builder.DbVersion()
|
dbVersion := qgen.Builder.DbVersion()
|
||||||
upDuration := time.Since(c.StartTime)
|
upDur := time.Since(c.StartTime)
|
||||||
hours := int(upDuration.Hours())
|
hours := int(upDur.Hours())
|
||||||
mins := int(upDuration.Minutes())
|
mins := int(upDur.Minutes())
|
||||||
secs := int(upDuration.Seconds())
|
secs := int(upDur.Seconds())
|
||||||
var uptime string
|
var uptime string
|
||||||
if hours > 24 {
|
if hours > 24 {
|
||||||
days := 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?
|
// TODO: Fetch the adapter from Builder rather than getting it from a global?
|
||||||
goroutines := runtime.NumGoroutine()
|
goroutines := runtime.NumGoroutine()
|
||||||
cpus := runtime.NumCPU()
|
cpus := runtime.NumCPU()
|
||||||
|
httpConns := c.ConnWatch.Count()
|
||||||
|
|
||||||
debugTasks := c.DebugPageTasks{c.ScheduledHalfSecondTaskCount(), c.ScheduledSecondTaskCount(), c.ScheduledFifteenMinuteTaskCount(), c.ScheduledHourTaskCount(), c.ShutdownTaskCount()}
|
debugTasks := c.DebugPageTasks{c.ScheduledHalfSecondTaskCount(), c.ScheduledSecondTaskCount(), c.ScheduledFifteenMinuteTaskCount(), c.ScheduledHourTaskCount(), c.ShutdownTaskCount()}
|
||||||
var memStats runtime.MemStats
|
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 tlen, ulen, rlen int
|
||||||
var tcap, ucap, rcap int
|
var tcap, ucap, rcap int
|
||||||
tcache := c.Topics.GetCache()
|
tc := c.Topics.GetCache()
|
||||||
if tcache != nil {
|
if tc != nil {
|
||||||
tlen = tcache.Length()
|
tlen, tcap = tc.Length(), tc.GetCapacity()
|
||||||
tcap = tcache.GetCapacity()
|
|
||||||
}
|
}
|
||||||
ucache := c.Users.GetCache()
|
uc := c.Users.GetCache()
|
||||||
if ucache != nil {
|
if uc != nil {
|
||||||
ulen = ucache.Length()
|
ulen, ucap = uc.Length(), uc.GetCapacity()
|
||||||
ucap = ucache.GetCapacity()
|
|
||||||
}
|
}
|
||||||
rcache := c.Rstore.GetCache()
|
rc := c.Rstore.GetCache()
|
||||||
if rcache != nil {
|
if rc != nil {
|
||||||
rlen = rcache.Length()
|
rlen, rcap = rc.Length(), rc.GetCapacity()
|
||||||
rcap = rcache.GetCapacity()
|
|
||||||
}
|
}
|
||||||
topicListThawed := c.TopicListThaw.Thawed()
|
topicListThawed := c.TopicListThaw.Thawed()
|
||||||
|
|
||||||
|
@ -121,12 +119,13 @@ func Debug(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
|
||||||
if fErr != nil {
|
if fErr != nil {
|
||||||
return c.InternalError(fErr, w, r)
|
return c.InternalError(fErr, w, r)
|
||||||
}
|
}
|
||||||
|
// TODO: How can we measure this without freezing up the entire page?
|
||||||
//gitSize, _ := c.DirSize("./.git")
|
//gitSize, _ := c.DirSize("./.git")
|
||||||
gitSize := 0
|
gitSize := 0
|
||||||
|
|
||||||
debugDisk := c.DebugPageDisk{staticSize, attachSize, uploadsSize, logsSize, backupsSize, gitSize}
|
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})
|
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 {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
// TODO: Move this even earlier?
|
||||||
js := r.PostFormValue("js") == "1"
|
js := r.PostFormValue("js") == "1"
|
||||||
if !u.Perms.ManageForums {
|
if !u.Perms.ManageForums {
|
||||||
return c.NoPermissionsJSQ(w, r, u, js)
|
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")
|
basePage.Header.AddScriptAsync("panel_forum_edit.js")
|
||||||
|
|
||||||
forum, err := c.Forums.Get(fid)
|
f, err := c.Forums.Get(fid)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u)
|
return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
if forum.Preset == "" {
|
if f.Preset == "" {
|
||||||
forum.Preset = "custom"
|
f.Preset = "custom"
|
||||||
}
|
}
|
||||||
|
|
||||||
glist, err := c.Groups.GetAll()
|
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")
|
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})
|
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)
|
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 {
|
if err == sql.ErrNoRows {
|
||||||
return c.LocalErrorJSQ("This forum doesn't exist", w, r, u, js)
|
return c.LocalErrorJSQ("This forum doesn't exist", w, r, u, js)
|
||||||
} else if err != nil {
|
} 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"))
|
permPreset := c.StripInvalidGroupForumPreset(r.PostFormValue("perm_preset"))
|
||||||
err = forum.SetPreset(permPreset, gid)
|
err = f.SetPreset(permPreset, gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.LocalErrorJSQ(err.Error(), w, r, u, js)
|
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
|
// 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, "-")
|
params := strings.Split(paramList, "-")
|
||||||
if len(params) != 2 {
|
if len(params) != 2 {
|
||||||
return fid, gid, errors.New("Parameter count mismatch")
|
return fid, gid, errors.New("Parameter count mismatch")
|
||||||
}
|
}
|
||||||
|
fid, e = strconv.Atoi(params[0])
|
||||||
fid, err = strconv.Atoi(params[0])
|
if e != nil {
|
||||||
if err != nil {
|
|
||||||
return fid, gid, errors.New("The provided Forum ID is not a valid number.")
|
return fid, gid, errors.New("The provided Forum ID is not a valid number.")
|
||||||
}
|
}
|
||||||
|
gid, e = strconv.Atoi(params[1])
|
||||||
gid, err = strconv.Atoi(params[1])
|
if e != nil {
|
||||||
if err != nil {
|
e = errors.New("The provided Group ID is not a valid number.")
|
||||||
err = errors.New("The provided Group ID is not a valid number.")
|
|
||||||
}
|
}
|
||||||
|
return fid, gid, e
|
||||||
return fid, gid, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, u *c.User, paramList string) c.RouteError {
|
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)
|
return c.LocalError(err.Error(), w, r, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
forum, err := c.Forums.Get(fid)
|
f, err := c.Forums.Get(fid)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u)
|
return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u)
|
||||||
} else if err != nil {
|
} 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)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
extractPerm := func(name string) bool {
|
ep := func(name string) bool {
|
||||||
pvalue := r.PostFormValue("perm-" + name)
|
pvalue := r.PostFormValue("perm-" + name)
|
||||||
return (pvalue == "1")
|
return (pvalue == "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Generate this code?
|
// TODO: Generate this code?
|
||||||
fp.ViewTopic = extractPerm("ViewTopic")
|
fp.ViewTopic = ep("ViewTopic")
|
||||||
fp.LikeItem = extractPerm("LikeItem")
|
fp.LikeItem = ep("LikeItem")
|
||||||
fp.CreateTopic = extractPerm("CreateTopic")
|
fp.CreateTopic = ep("CreateTopic")
|
||||||
fp.EditTopic = extractPerm("EditTopic")
|
fp.EditTopic = ep("EditTopic")
|
||||||
fp.DeleteTopic = extractPerm("DeleteTopic")
|
fp.DeleteTopic = ep("DeleteTopic")
|
||||||
fp.CreateReply = extractPerm("CreateReply")
|
fp.CreateReply = ep("CreateReply")
|
||||||
fp.EditReply = extractPerm("EditReply")
|
fp.EditReply = ep("EditReply")
|
||||||
fp.DeleteReply = extractPerm("DeleteReply")
|
fp.DeleteReply = ep("DeleteReply")
|
||||||
fp.PinTopic = extractPerm("PinTopic")
|
fp.PinTopic = ep("PinTopic")
|
||||||
fp.CloseTopic = extractPerm("CloseTopic")
|
fp.CloseTopic = ep("CloseTopic")
|
||||||
fp.MoveTopic = extractPerm("MoveTopic")
|
fp.MoveTopic = ep("MoveTopic")
|
||||||
|
|
||||||
err = forum.SetPerms(&fp, "custom", gid)
|
err = f.SetPerms(&fp, "custom", gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.LocalErrorJSQ(err.Error(), w, r, u, js)
|
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)
|
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?
|
// TODO: What if this list gets too long?
|
||||||
filters, err := c.WordFilters.GetAll()
|
filters, e := c.WordFilters.GetAll()
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(e, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := c.PanelPage{basePage, tList, filters}
|
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
|
// 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"))
|
replace := strings.TrimSpace(r.PostFormValue("replace"))
|
||||||
|
|
||||||
wfid, err := c.WordFilters.Create(find, replace)
|
wfid, e := c.WordFilters.Create(find, replace)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return c.InternalErrorJSQ(err, w, r, js)
|
return c.InternalErrorJSQ(e, w, r, js)
|
||||||
}
|
}
|
||||||
err = c.AdminLogs.Create("create", wfid, "word_filter", u.GetIP(), u.ID)
|
e = c.AdminLogs.Create("create", wfid, "word_filter", u.GetIP(), u.ID)
|
||||||
if err != nil {
|
if e != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(e, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
return successRedirect("/panel/settings/word-filters/", w, r, js)
|
return successRedirect("/panel/settings/word-filters/", w, r, js)
|
||||||
|
|
|
@ -13,8 +13,8 @@ import (
|
||||||
|
|
||||||
func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList {
|
func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList {
|
||||||
wsTopicList := make([]*c.WsTopicsRow, len(topicList))
|
wsTopicList := make([]*c.WsTopicsRow, len(topicList))
|
||||||
for i, topicRow := range topicList {
|
for i, tr := range topicList {
|
||||||
wsTopicList[i] = topicRow.WebSockets()
|
wsTopicList[i] = tr.WebSockets()
|
||||||
}
|
}
|
||||||
return &c.WsTopicList{wsTopicList, lastPage, 0}
|
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 ('meta_desc','','html-attribute');
|
||||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('rapid_loading','1','bool');
|
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]) 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 [themes] ([uname],[default]) VALUES ('cosora',1);
|
||||||
INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,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');
|
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 (4,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (5,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 [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 [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,'::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,'');
|
||||||
INSERT INTO [menus] () VALUES ();
|
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],[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);
|
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 ('meta_desc','','html-attribute');
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('rapid_loading','1','bool');
|
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`) 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 `themes`(`uname`,`default`) VALUES ('cosora',1);
|
||||||
INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,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');
|
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 (4,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (5,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 `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 `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,'::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,'');
|
||||||
INSERT INTO `menus`() VALUES ();
|
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`,`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);
|
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);
|
||||||
|
|
|
@ -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 ('meta_desc','','html-attribute');
|
||||||
INSERT INTO "settings"("name","content","type") VALUES ('rapid_loading','1','bool');
|
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") 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 "themes"("uname","default") VALUES ('cosora',1);
|
||||||
INSERT INTO "emails"("email","uid","validated") VALUES ('admin@localhost',1,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');
|
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 (4,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (5,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 "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 "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,'::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,'');
|
||||||
INSERT INTO "menus"() VALUES ();
|
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","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);
|
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,
|
`lastActiveAt` timestamp not null,
|
||||||
`session` varchar (200) DEFAULT '' not null,
|
`session` varchar (200) DEFAULT '' not null,
|
||||||
`last_ip` 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,
|
`enable_embeds` int DEFAULT -1 not null,
|
||||||
`email` varchar (200) DEFAULT '' not null,
|
`email` varchar (200) DEFAULT '' not null,
|
||||||
`avatar` varchar (100) 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_prefix` varchar (20) DEFAULT '' not null,
|
||||||
`url_name` varchar (100) DEFAULT '' not null,
|
`url_name` varchar (100) DEFAULT '' not null,
|
||||||
`level` smallint DEFAULT 0 not null,
|
`level` smallint DEFAULT 0 not null,
|
||||||
|
|
|
@ -5,6 +5,6 @@ CREATE TABLE "widgets" (
|
||||||
`type` varchar (100) not null,
|
`type` varchar (100) not null,
|
||||||
`active` boolean DEFAULT 0 not null,
|
`active` boolean DEFAULT 0 not null,
|
||||||
`location` varchar (100) not null,
|
`location` varchar (100) not null,
|
||||||
`data` text DEFAULT '' not null,
|
`data` text not null,
|
||||||
primary key(`wid`)
|
primary key(`wid`)
|
||||||
);
|
);
|
|
@ -12,7 +12,7 @@
|
||||||
{{if .Desc}}
|
{{if .Desc}}
|
||||||
<span class="rowsmall"itemprop="description">{{.Desc}}</span>
|
<span class="rowsmall"itemprop="description">{{.Desc}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="rowsmall forum_nodesc">{{lang "forums_no_description"}}</span>
|
<span class="rowsmall forum_nodesc">{{lang "forums_no_desc"}}</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</span>
|
</span>
|
||||||
<span class="forum_right shift_right">
|
<span class="forum_right shift_right">
|
||||||
|
|
|
@ -13,16 +13,16 @@
|
||||||
{{template "panel_debug_stat_head.html" "panel_debug_adapter_label"}}
|
{{template "panel_debug_stat_head.html" "panel_debug_adapter_label"}}
|
||||||
{{/** TODO: Use this for active database connections when Go 1.11 lands **/}}
|
{{/** TODO: Use this for active database connections when Go 1.11 lands **/}}
|
||||||
{{template "panel_debug_stat_head_q.html"}}
|
{{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.html" .DBAdapter}}
|
||||||
{{template "panel_debug_stat_q.html"}}
|
{{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_goroutine_count_label"}}
|
||||||
{{template "panel_debug_stat_head.html" "panel_debug_cpu_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" .Goroutines}}
|
||||||
{{template "panel_debug_stat.html" .CPUs}}
|
{{template "panel_debug_stat.html" .CPUs}}
|
||||||
{{template "panel_debug_stat_q.html"}}
|
{{template "panel_debug_stat.html" .HttpConns}}
|
||||||
</div>
|
</div>
|
||||||
{{template "panel_debug_subhead.html" "panel_debug_tasks"}}
|
{{template "panel_debug_subhead.html" "panel_debug_tasks"}}
|
||||||
<div id="panel_debug"class="colstack_grid">
|
<div id="panel_debug"class="colstack_grid">
|
||||||
|
|
|
@ -1,45 +1,45 @@
|
||||||
<div class="colstack_item colstack_head">
|
<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>
|
||||||
<div id="panel_forum"class="colstack_item the_form">
|
<div id="panel_forum"class="colstack_item the_form">
|
||||||
<form action="/panel/forums/edit/submit/{{.ID}}?s={{.CurrentUser.Session}}"method="post">
|
<form action="/panel/forums/edit/submit/{{.ID}}?s={{.CurrentUser.Session}}"method="post">
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
<div class="formitem formlabel"><a>{{lang "panel_forum_name"}}</a></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 class="formitem"><input name="forum_name"type="text"value="{{.Name}}"placeholder="{{lang "panel.forum_name_placeholder"}}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
<div class="formitem formlabel"><a>{{lang "panel_forum_description"}}</a></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_description_placeholder"}}"></div>
|
<div class="formitem"><input name="forum_desc"type="text"value="{{.Desc}}"placeholder="{{lang "panel.forum_desc_placeholder"}}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<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">
|
<div class="formitem"><select name="forum_active">
|
||||||
<option{{if .Active}} selected{{end}} value=1>{{lang "option_yes"}}</option>
|
<option{{if .Active}} selected{{end}} value=1>{{lang "option_yes"}}</option>
|
||||||
<option{{if not .Active}} selected{{end}} value=0>{{lang "option_no"}}</option>
|
<option{{if not .Active}} selected{{end}} value=0>{{lang "option_no"}}</option>
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<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">
|
<div class="formitem">
|
||||||
<select name="forum_preset">
|
<select name="forum_preset">
|
||||||
<option{{if eq .Preset "all"}} selected{{end}} value="all">{{lang "panel_preset_everyone"}}</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 "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 "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 "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 "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 "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 "custom"}} selected{{end}} value="custom">{{lang "panel.preset_custom"}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
<h1>{{lang "panel_forum_permissions_head"}}</h1>
|
<h1>{{lang "panel.forum_permissions_head"}}</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="forum_quick_perms"class="colstack_item rowlist formlist the_form">
|
<div id="forum_quick_perms"class="colstack_item rowlist formlist the_form">
|
||||||
|
@ -48,13 +48,76 @@
|
||||||
<div class="formitem editable_parent">
|
<div class="formitem editable_parent">
|
||||||
<a>{{.Group.Name}}</a>
|
<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}}">
|
<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">
|
<div class="panel_floater">
|
||||||
<span data-field="perm_preset"data-type="list"data-value="{{.Preset}}"class="editable_block perm_preset perm_preset_{{.Preset}}"></span>
|
<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/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>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</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="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>
|
||||||
<form action="/panel/forums/edit/perms/adv/submit/{{.ForumID}}-{{.GroupID}}?s={{.CurrentUser.Session}}"method="post">
|
<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">
|
<div class="colstack_item rowlist formlist the_form panel_forum_perms">
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>{{end}}
|
</div>{{end}}
|
||||||
<div class="formrow">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
|
@ -40,8 +40,8 @@
|
||||||
<div class="formitem"><input name="name" type="text" placeholder="{{lang "panel.forums_create_name"}}"></div>
|
<div class="formitem"><input name="name" type="text" placeholder="{{lang "panel.forums_create_name"}}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
<div class="formitem formlabel"><a>{{lang "panel.forums_create_description_label"}}</a></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_description"}}"></div>
|
<div class="formitem"><input name="desc" type="text" placeholder="{{lang "panel.forums_create_desc"}}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
<div class="formitem formlabel"><a>{{lang "panel.forums_active_label"}}</a></div>
|
<div class="formitem formlabel"><a>{{lang "panel.forums_active_label"}}</a></div>
|
||||||
|
@ -53,13 +53,13 @@
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
<div class="formitem formlabel"><a>{{lang "panel.forums_preset_label"}}</a></div>
|
<div class="formitem formlabel"><a>{{lang "panel.forums_preset_label"}}</a></div>
|
||||||
<div class="formitem"><select name="preset">
|
<div class="formitem"><select name="preset">
|
||||||
<option selected value="all">{{lang "panel_preset_everyone"}}</option>
|
<option selected value="all">{{lang "panel.preset_everyone"}}</option>
|
||||||
<option value="announce">{{lang "panel_preset_announcements"}}</option>
|
<option value="announce">{{lang "panel.preset_announcements"}}</option>
|
||||||
<option value="members">{{lang "panel_preset_member_only"}}</option>
|
<option value="members">{{lang "panel.preset_member_only"}}</option>
|
||||||
<option value="staff">{{lang "panel_preset_staff_only"}}</option>
|
<option value="staff">{{lang "panel.preset_staff_only"}}</option>
|
||||||
<option value="admins">{{lang "panel_preset_admin_only"}}</option>
|
<option value="admins">{{lang "panel.preset_admin_only"}}</option>
|
||||||
<option value="archive">{{lang "panel_preset_archive"}}</option>
|
<option value="archive">{{lang "panel.preset_archive"}}</option>
|
||||||
<option value="custom">{{lang "panel_preset_custom"}}</option>
|
<option value="custom">{{lang "panel.preset_custom"}}</option>
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
|
|
Loading…
Reference in New Issue