From 4d8c97812de0b83d8a906273000d81a8629fb782 Mon Sep 17 00:00:00 2001 From: Azareal Date: Wed, 14 Aug 2019 20:39:04 +1000 Subject: [PATCH] More conversations work. Add support for AS in columns for SimpleInnerJoin. Add a referrer policy to improve privacy a little. Shorten /static/ to /s/ since it comes up so much. Remove some obsolete code. Shorten some variable names. Reduce the amount of boilerplate in the patcher. Added the RefNoTrack and RefNoRef privacy config settings. You may need to run the updater / patcher for this commit. --- cmd/query_gen/tables.go | 4 +- common/conversations.go | 115 +++-- common/convos_posts.go | 25 +- common/files.go | 6 +- common/pages.go | 47 +- common/site.go | 3 + common/theme.go | 2 +- common/user.go | 2 +- docs/configuration.md | 4 + experimental/new-replybit.html | 4 +- gen_router.go | 434 ++++++++++++------ general_test.go | 4 +- langs/english.json | 2 + main.go | 4 +- patcher/patches.go | 209 +++++---- public/init.js | 10 +- public/jquery-emojiarea/emojis.js | 2 +- query_gen/mysql.go | 51 +- router_gen/main.go | 27 +- router_gen/routes.go | 15 +- routes/common.go | 16 +- routes/convos.go | 85 +++- routes/reports.go | 29 +- schema/mssql/query_conversations.sql | 8 + .../query_conversations_participants.sql | 4 + schema/mssql/query_conversations_posts.sql | 8 + schema/mysql/query_conversations.sql | 8 + .../query_conversations_participants.sql | 4 + schema/mysql/query_conversations_posts.sql | 8 + schema/pgsql/query_conversations.sql | 8 + .../query_conversations_participants.sql | 4 + schema/pgsql/query_conversations_posts.sql | 8 + templates/account_menu.html | 2 + templates/convo.html | 6 +- templates/convo_row.html | 21 + templates/convo_row_alt.html | 26 ++ templates/convos.html | 2 +- templates/header.html | 12 +- templates/panel_themes.html | 14 +- templates/profile.html | 4 +- templates/profile_comments_row.html | 24 +- templates/profile_comments_row_alt.html | 38 +- templates/topic.html | 2 +- templates/topic_alt_quick_reply.html | 2 +- templates/topic_alt_userinfo.html | 2 +- templates/topic_poll.html | 2 +- templates/topic_posts.html | 2 +- themes/cosora/public/convo.css | 0 themes/cosora/public/misc.js | 2 +- themes/nox/public/convo.css | 30 ++ themes/nox/public/main.css | 4 + themes/nox/public/profile.css | 2 - themes/shadow/public/convo.css | 0 themes/tempra_simple/public/convo.css | 0 54 files changed, 901 insertions(+), 456 deletions(-) create mode 100644 schema/mssql/query_conversations.sql create mode 100644 schema/mssql/query_conversations_participants.sql create mode 100644 schema/mssql/query_conversations_posts.sql create mode 100644 schema/mysql/query_conversations.sql create mode 100644 schema/mysql/query_conversations_participants.sql create mode 100644 schema/mysql/query_conversations_posts.sql create mode 100644 schema/pgsql/query_conversations.sql create mode 100644 schema/pgsql/query_conversations_participants.sql create mode 100644 schema/pgsql/query_conversations_posts.sql create mode 100644 templates/convo_row.html create mode 100644 templates/convo_row_alt.html create mode 100644 themes/cosora/public/convo.css create mode 100644 themes/nox/public/convo.css create mode 100644 themes/shadow/public/convo.css create mode 100644 themes/tempra_simple/public/convo.css diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 76a47fea..f6918ae0 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -362,7 +362,7 @@ func createTables(adapter qgen.Adapter) error { ) //columns("participants, createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?") - /*qgen.Install.CreateTable("conversations", "", "", + qgen.Install.CreateTable("conversations", "", "", []tC{ tC{"cid", "int", 0, false, true, ""}, tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -393,7 +393,7 @@ func createTables(adapter qgen.Adapter) error { tC{"uid", "int", 0, false, false, ""}, tC{"cid", "int", 0, false, false, ""}, }, nil, - )*/ + ) qgen.Install.CreateTable("activity_stream_matches", "", "", []tC{ diff --git a/common/conversations.go b/common/conversations.go index a80c368b..6f9b8873 100644 --- a/common/conversations.go +++ b/common/conversations.go @@ -1,8 +1,9 @@ package common import ( - "time" "errors" + "time" + //"strconv" "database/sql" @@ -17,62 +18,63 @@ var Convos ConversationStore var convoStmts ConvoStmts type ConvoStmts struct { - getPosts *sql.Stmt + fetchPost *sql.Stmt + getPosts *sql.Stmt countPosts *sql.Stmt - edit *sql.Stmt - create *sql.Stmt - delete *sql.Stmt + edit *sql.Stmt + create *sql.Stmt + delete *sql.Stmt - editPost *sql.Stmt + editPost *sql.Stmt createPost *sql.Stmt + deletePost *sql.Stmt } -/*func init() { +func init() { DbInits.Add(func(acc *qgen.Accumulator) error { convoStmts = ConvoStmts{ - getPosts: acc.Select("conversations_posts").Columns("pid, body, post, createdBy").Where("cid = ?").Limit("?,?").Prepare(), + fetchPost: acc.Select("conversations_posts").Columns("cid, body, post, createdBy").Where("pid = ?").Prepare(), + getPosts: acc.Select("conversations_posts").Columns("pid, body, post, createdBy").Where("cid = ?").Limit("?,?").Prepare(), countPosts: acc.Count("conversations_posts").Where("cid = ?").Prepare(), - //edit: acc.Update("conversations").Set("participants = ?, lastReplyBy = ?, lastReplyAt = ?").Where("cid = ?").Prepare(), - edit: acc.Update("conversations").Set("lastReplyBy = ?, lastReplyAt = ?").Where("cid = ?").Prepare(), - //create: acc.Insert("conversations").Columns("participants, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), - create: acc.Insert("conversations").Columns("createdAt, lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), + edit: acc.Update("conversations").Set("lastReplyBy = ?, lastReplyAt = ?").Where("cid = ?").Prepare(), + create: acc.Insert("conversations").Columns("createdAt, lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), - editPost: acc.Update("conversations_posts").Set("body = ?, post = ?").Where("cid = ?").Prepare(), + editPost: acc.Update("conversations_posts").Set("body = ?, post = ?").Where("cid = ?").Prepare(), createPost: acc.Insert("conversations_posts").Columns("cid, body, post, createdBy").Fields("?,?,?,?").Prepare(), + deletePost: acc.Delete("conversations_posts").Where("pid = ?").Prepare(), } return acc.FirstError() }) -}*/ +} type Conversation struct { - ID int - //Participants string - CreatedBy int - CreatedAt time.Time + ID int + CreatedBy int + CreatedAt time.Time LastReplyBy int LastReplyAt time.Time } -func (co *Conversation) Posts(offset int) (posts []*ConversationPost, err error) { - rows, err := convoStmts.getPosts.Query(co.ID, offset, Config.ItemsPerPage) +func (co *Conversation) Posts(offset, itemsPerPage int) (posts []*ConversationPost, err error) { + rows, err := convoStmts.getPosts.Query(co.ID, offset, itemsPerPage) if err != nil { return nil, err } defer rows.Close() for rows.Next() { - convo := &ConversationPost{CID: co.ID} - err := rows.Scan(&convo.ID, &convo.Body, &convo.Post, &convo.CreatedBy) + p := &ConversationPost{CID: co.ID} + err := rows.Scan(&p.ID, &p.Body, &p.Post, &p.CreatedBy) if err != nil { return nil, err } - convo, err = ConvoPostProcess.OnLoad(convo) + p, err = ConvoPostProcess.OnLoad(p) if err != nil { return nil, err } - posts = append(posts, convo) + posts = append(posts, p) } - + return posts, rows.Err() } @@ -85,12 +87,12 @@ func (co *Conversation) PostsCount() (count int) { } func (co *Conversation) Update() error { - _, err := convoStmts.edit.Exec(/*co.Participants, */co.CreatedAt, co.LastReplyBy, co.LastReplyAt, co.ID) + _, err := convoStmts.edit.Exec(co.CreatedAt, co.LastReplyBy, co.LastReplyAt, co.ID) return err } func (co *Conversation) Create() (int, error) { - res, err := convoStmts.create.Exec(/*co.Participants*/) + res, err := convoStmts.create.Exec() if err != nil { return 0, err } @@ -109,39 +111,34 @@ type ConversationStore interface { } type DefaultConversationStore struct { - get *sql.Stmt - getUser *sql.Stmt - getUserCount *sql.Stmt - delete *sql.Stmt - deletePosts *sql.Stmt - create *sql.Stmt + get *sql.Stmt + getUser *sql.Stmt + getUserCount *sql.Stmt + delete *sql.Stmt + deletePosts *sql.Stmt + deleteParticipants *sql.Stmt + create *sql.Stmt addParticipant *sql.Stmt - count *sql.Stmt + count *sql.Stmt } func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConversationStore, error) { return &DefaultConversationStore{ - //get: acc.Select("conversations").Columns("participants, createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?").Prepare(), - get: acc.Select("conversations").Columns("createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?").Prepare(), - - //("replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.attachCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "replies.rid ASC", "?,?") - //(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) - - //getUser: acc.SimpleLeftJoin("conversations_participants AS cp","conversations AS c","c.cid, c.participants, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt","cp.cid = c.cid","cp.uid = ?","c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC","?,?"), - getUser: acc.SimpleLeftJoin("conversations_participants AS cp","conversations AS c","c.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt","cp.cid = c.cid","cp.uid = ?","c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC","?,?"), - getUserCount: acc.Count("conversations_participants").Where("uid = ?").Prepare(), - delete: acc.Delete("conversations").Where("cid = ?").Prepare(), - deletePosts: acc.Delete("conversations_posts").Where("cid = ?").Prepare(), - //create: acc.Insert("conversations").Columns("participants, createdBy, createdAt, lastReplyAt").Fields("?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), - create: acc.Insert("conversations").Columns("createdBy, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), + get: acc.Select("conversations").Columns("createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?").Prepare(), + getUser: acc.SimpleInnerJoin("conversations_participants AS cp", "conversations AS c", "cp.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt", "cp.cid = c.cid", "cp.uid = ?", "c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC", "?,?"), + getUserCount: acc.Count("conversations_participants").Where("uid = ?").Prepare(), + delete: acc.Delete("conversations").Where("cid = ?").Prepare(), + deletePosts: acc.Delete("conversations_posts").Where("cid = ?").Prepare(), + deleteParticipants: acc.Delete("conversations_participants").Where("cid = ?").Prepare(), + create: acc.Insert("conversations").Columns("createdBy, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), addParticipant: acc.Insert("conversations_participants").Columns("uid, cid").Fields("?,?").Prepare(), - count: acc.Count("conversations").Prepare(), + count: acc.Count("conversations").Prepare(), }, acc.FirstError() } func (s *DefaultConversationStore) Get(id int) (*Conversation, error) { convo := &Conversation{ID: id} - err := s.get.QueryRow(id).Scan(/*&convo.Participants, */&convo.CreatedBy, &convo.CreatedAt, &convo.LastReplyBy, &convo.LastReplyAt) + err := s.get.QueryRow(id).Scan(&convo.CreatedBy, &convo.CreatedAt, &convo.LastReplyBy, &convo.LastReplyAt) return convo, err } @@ -154,7 +151,7 @@ func (s *DefaultConversationStore) GetUser(uid int, offset int) (cos []*Conversa for rows.Next() { co := &Conversation{} - err := rows.Scan(&co.ID, /*&co.Participants,*/ &co.CreatedBy, &co.CreatedAt, &co.LastReplyBy, &co.LastReplyAt) + err := rows.Scan(&co.ID, &co.CreatedBy, &co.CreatedAt, &co.LastReplyBy, &co.LastReplyAt) if err != nil { return nil, err } @@ -179,6 +176,10 @@ func (s *DefaultConversationStore) Delete(id int) error { return err } _, err = s.deletePosts.Exec(id) + if err != nil { + return err + } + _, err = s.deleteParticipants.Exec(id) return err } @@ -186,13 +187,7 @@ func (s *DefaultConversationStore) Create(content string, createdBy int, partici if len(participants) == 0 { return 0, errors.New("no participants set") } - /*var pstr string - for _, parti := range participants { - pstr += strconv.Itoa(parti) + "," - } - pstr = pstr[:len(pstr)-1]*/ - - res, err := s.create.Exec(createdBy/*, pstr*/) + res, err := s.create.Exec(createdBy) if err != nil { return 0, err } @@ -211,12 +206,12 @@ func (s *DefaultConversationStore) Create(content string, createdBy int, partici } for _, p := range participants { - _, err := s.addParticipant.Exec(p,lastID) + _, err := s.addParticipant.Exec(p, lastID) if err != nil { return 0, err } } - _, err = s.addParticipant.Exec(createdBy,lastID) + _, err = s.addParticipant.Exec(createdBy, lastID) if err != nil { return 0, err } @@ -231,4 +226,4 @@ func (s *DefaultConversationStore) Count() (count int) { LogError(err) } return count -} \ No newline at end of file +} diff --git a/common/convos_posts.go b/common/convos_posts.go index a40e29bb..ab153538 100644 --- a/common/convos_posts.go +++ b/common/convos_posts.go @@ -1,11 +1,11 @@ package common import ( - "io" - "encoding/hex" "crypto/aes" "crypto/cipher" "crypto/rand" + "encoding/hex" + "io" ) var ConvoPostProcess ConvoPostProcessor = NewDefaultConvoPostProcessor() @@ -99,13 +99,17 @@ func (pr *AesConvoPostProcessor) OnSave(co *ConversationPost) (*ConversationPost } type ConversationPost struct { - ID int - CID int - Body string - Post string // aes, '' + ID int + CID int + Body string + Post string // aes, '' CreatedBy int } +func (co *ConversationPost) Fetch() error { + return convoStmts.fetchPost.QueryRow(co.ID).Scan(&co.CID, &co.Body, &co.Post, &co.CreatedBy) +} + func (co *ConversationPost) Update() error { lco, err := ConvoPostProcess.OnSave(co) if err != nil { @@ -122,11 +126,16 @@ func (co *ConversationPost) Create() (int, error) { return 0, err } //GetHookTable().VhookNoRet("convo_post_create", lco) - res, err := convoStmts.createPost.Exec(lco.CID, lco.Body, lco.Post) + res, err := convoStmts.createPost.Exec(lco.CID, lco.Body, lco.Post, lco.CreatedBy) if err != nil { return 0, err } lastID, err := res.LastInsertId() return int(lastID), err -} \ No newline at end of file +} + +func (co *ConversationPost) Delete() error { + _, err := convoStmts.deletePost.Exec(co.ID) + return err +} diff --git a/common/files.go b/common/files.go index c7893215..bbe19dc4 100644 --- a/common/files.go +++ b/common/files.go @@ -240,7 +240,7 @@ func (list SFileList) JSTmplInit() error { hasher.Write(data) checksum := hex.EncodeToString(hasher.Sum(nil)) - list.Set("/static/"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) + list.Set("/s/"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) DebugLogf("Added the '%s' static file.", path) return nil @@ -285,7 +285,7 @@ func (list SFileList) Init() error { } } - list.Set("/static/"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)}) + list.Set("/s/"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)}) DebugLogf("Added the '%s' static file.", path) return nil @@ -318,7 +318,7 @@ func (list SFileList) Add(path string, prefix string) error { hasher.Write(data) checksum := hex.EncodeToString(hasher.Sum(nil)) - list.Set("/static"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) + list.Set("/s"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) DebugLogf("Added the '%s' static file", path) return nil diff --git a/common/pages.go b/common/pages.go index 22011f34..fdacb51c 100644 --- a/common/pages.go +++ b/common/pages.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/Azareal/Gosora/common/phrases" + p "github.com/Azareal/Gosora/common/phrases" ) /*type HResource struct { @@ -15,7 +15,7 @@ import ( Hash string }*/ -// TODO: Allow resources in spots other than /static/ and possibly even external domains (e.g. CDNs) +// TODO: Allow resources in spots other than /s/ and possibly even external domains (e.g. CDNs) // TODO: Preload Trumboyg on Cosora on the forum list type Header struct { Title string @@ -50,9 +50,9 @@ type Header struct { ExtData ExtData } -func (header *Header) AddScript(name string) { +func (h *Header) AddScript(name string) { // TODO: Use a secondary static file map to avoid this concatenation? - fname := "/static/" + name + fname := "/s/" + name var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) @@ -64,11 +64,11 @@ func (header *Header) AddScript(name string) { oname = name } //log.Print("oname:", oname) - header.Scripts = append(header.Scripts, oname) + h.Scripts = append(h.Scripts, oname) } -func (header *Header) AddPreScriptAsync(name string) { - fname := "/static/" + name +func (h *Header) AddPreScriptAsync(name string) { + fname := "/s/" + name var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) @@ -79,11 +79,11 @@ func (header *Header) AddPreScriptAsync(name string) { if oname == "" { oname = name } - header.PreScriptsAsync = append(header.PreScriptsAsync, oname) + h.PreScriptsAsync = append(h.PreScriptsAsync, oname) } -func (header *Header) AddScriptAsync(name string) { - fname := "/static/" + name +func (h *Header) AddScriptAsync(name string) { + fname := "/s/" + name var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) @@ -94,15 +94,15 @@ func (header *Header) AddScriptAsync(name string) { if oname == "" { oname = name } - header.ScriptsAsync = append(header.ScriptsAsync, oname) + h.ScriptsAsync = append(h.ScriptsAsync, oname) } -/*func (header *Header) Preload(name string) { - header.Preload = append(header.Preload, name) +/*func (h *Header) Preload(name string) { + h.Preload = append(h.Preload, name) }*/ -func (header *Header) AddSheet(name string) { - fname := "/static/" + name +func (h *Header) AddSheet(name string) { + fname := "/s/" + name var oname string if fname[0] == '/' && fname[1] != '/' { file, ok := StaticFiles.Get(fname) @@ -113,11 +113,11 @@ func (header *Header) AddSheet(name string) { if oname == "" { oname = name } - header.Stylesheets = append(header.Stylesheets, oname) + h.Stylesheets = append(h.Stylesheets, oname) } -func (header *Header) AddNotice(name string) { - header.NoticeList = append(header.NoticeList, phrases.GetNoticePhrase(name)) +func (h *Header) AddNotice(name string) { + h.NoticeList = append(h.NoticeList, p.GetNoticePhrase(name)) } // TODO: Add this to routes which don't use templates. E.g. Json APIs. @@ -289,9 +289,18 @@ type ConvoListPage struct { Paginator } +type ConvoViewRow struct { + *ConversationPost + User *User + ClassName string + ContentLines string +} + type ConvoViewPage struct { *Header - Posts []*ConversationPost + Convo *Conversation + Posts []ConvoViewRow + CanModify bool Paginator } diff --git a/common/site.go b/common/site.go index 682a3483..0e07bb5d 100644 --- a/common/site.go +++ b/common/site.go @@ -104,6 +104,9 @@ type config struct { DisableNoavatarRange bool DisableDefaultNoavatar bool + RefNoTrack bool + RefNoRef bool + Noavatar string // ? - Move this into the settings table? ItemsPerPage int // ? - Move this into the settings table? MaxTopicTitleLength int diff --git a/common/theme.go b/common/theme.go index 40fc42b5..a2c11b34 100644 --- a/common/theme.go +++ b/common/theme.go @@ -167,7 +167,7 @@ func (theme *Theme) AddThemeStaticFiles() error { hasher.Write(data) checksum := hex.EncodeToString(hasher.Sum(nil)) - StaticFiles.Set("/static/"+theme.Name+path, SFile{data, gzipData, checksum,theme.Name+path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) + StaticFiles.Set("/s/"+theme.Name+path, SFile{data, gzipData, checksum,theme.Name+path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) DebugLog("Added the '/" + theme.Name + path + "' static file for theme " + theme.Name + ".") return nil diff --git a/common/user.go b/common/user.go index d50ae1d9..69145b20 100644 --- a/common/user.go +++ b/common/user.go @@ -472,7 +472,7 @@ func buildNoavatar(uid int, width int) string { } } if !Config.DisableDefaultNoavatar && uid < 5 { - return "/static/n"+strconv.Itoa(uid)+"-"+strconv.Itoa(width)+".png?i=0" + return "/s/n"+strconv.Itoa(uid)+"-"+strconv.Itoa(width)+".png?i=0" } return strings.Replace(strings.Replace(Config.Noavatar, "{id}", strconv.Itoa(uid), 1), "{width}", strconv.Itoa(width), 1) } diff --git a/docs/configuration.md b/docs/configuration.md index a2610608..a4537fc8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -104,6 +104,10 @@ DisableNoavatarRange - This switch lets you disable the noavatar algorithm which DisableDefaultNoavatar - This switch lets you disable the default noavatar algorithm which may intercept noavatars for increased efficiency. Default: false +RefNoTrack - This switch disables tracking the referrers of users who click from another site to your site and the referrers of any requests to resources from other sites as-well. + +RefNoRef - This switch makes it so that if a user clicks on a link, then the incoming site won't know which site they're coming from. + NoAvatar - The default avatar to use for users when they don't have their own. The default for this may change in the near future to better utilise HTTP/2. Example: https://api.adorable.io/avatars/{width}/{id}.png ItemsPerPage - The number of posts, topics, etc. you want on each page. diff --git a/experimental/new-replybit.html b/experimental/new-replybit.html index 94606f6e..0df1aefc 100644 --- a/experimental/new-replybit.html +++ b/experimental/new-replybit.html @@ -1,9 +1,9 @@
-
 
+
 
Azareal
-
+
boo
Edit diff --git a/gen_router.go b/gen_router.go index 8293b34e..74f39f81 100644 --- a/gen_router.go +++ b/gen_router.go @@ -125,6 +125,14 @@ var RouteMap = map[string]interface{}{ "routes.AccountEditEmailTokenSubmit": routes.AccountEditEmailTokenSubmit, "routes.AccountLogins": routes.AccountLogins, "routes.LevelList": routes.LevelList, + "routes.Convos": routes.Convos, + "routes.ConvosCreate": routes.ConvosCreate, + "routes.Convo": routes.Convo, + "routes.ConvosCreateSubmit": routes.ConvosCreateSubmit, + "routes.ConvosDeleteSubmit": routes.ConvosDeleteSubmit, + "routes.ConvosCreateReplySubmit": routes.ConvosCreateReplySubmit, + "routes.ConvosDeleteReplySubmit": routes.ConvosDeleteReplySubmit, + "routes.ConvosEditReplySubmit": routes.ConvosEditReplySubmit, "routes.ViewProfile": routes.ViewProfile, "routes.BanUserSubmit": routes.BanUserSubmit, "routes.UnbanUser": routes.UnbanUser, @@ -278,53 +286,61 @@ var routeMapEnum = map[string]int{ "routes.AccountEditEmailTokenSubmit": 99, "routes.AccountLogins": 100, "routes.LevelList": 101, - "routes.ViewProfile": 102, - "routes.BanUserSubmit": 103, - "routes.UnbanUser": 104, - "routes.ActivateUser": 105, - "routes.IPSearch": 106, - "routes.CreateTopicSubmit": 107, - "routes.EditTopicSubmit": 108, - "routes.DeleteTopicSubmit": 109, - "routes.StickTopicSubmit": 110, - "routes.UnstickTopicSubmit": 111, - "routes.LockTopicSubmit": 112, - "routes.UnlockTopicSubmit": 113, - "routes.MoveTopicSubmit": 114, - "routes.LikeTopicSubmit": 115, - "routes.AddAttachToTopicSubmit": 116, - "routes.RemoveAttachFromTopicSubmit": 117, - "routes.ViewTopic": 118, - "routes.CreateReplySubmit": 119, - "routes.ReplyEditSubmit": 120, - "routes.ReplyDeleteSubmit": 121, - "routes.ReplyLikeSubmit": 122, - "routes.AddAttachToReplySubmit": 123, - "routes.RemoveAttachFromReplySubmit": 124, - "routes.ProfileReplyCreateSubmit": 125, - "routes.ProfileReplyEditSubmit": 126, - "routes.ProfileReplyDeleteSubmit": 127, - "routes.PollVote": 128, - "routes.PollResults": 129, - "routes.AccountLogin": 130, - "routes.AccountRegister": 131, - "routes.AccountLogout": 132, - "routes.AccountLoginSubmit": 133, - "routes.AccountLoginMFAVerify": 134, - "routes.AccountLoginMFAVerifySubmit": 135, - "routes.AccountRegisterSubmit": 136, - "routes.AccountPasswordReset": 137, - "routes.AccountPasswordResetSubmit": 138, - "routes.AccountPasswordResetToken": 139, - "routes.AccountPasswordResetTokenSubmit": 140, - "routes.DynamicRoute": 141, - "routes.UploadedFile": 142, - "routes.StaticFile": 143, - "routes.RobotsTxt": 144, - "routes.SitemapXml": 145, - "routes.OpenSearchXml": 146, - "routes.BadRoute": 147, - "routes.HTTPSRedirect": 148, + "routes.Convos": 102, + "routes.ConvosCreate": 103, + "routes.Convo": 104, + "routes.ConvosCreateSubmit": 105, + "routes.ConvosDeleteSubmit": 106, + "routes.ConvosCreateReplySubmit": 107, + "routes.ConvosDeleteReplySubmit": 108, + "routes.ConvosEditReplySubmit": 109, + "routes.ViewProfile": 110, + "routes.BanUserSubmit": 111, + "routes.UnbanUser": 112, + "routes.ActivateUser": 113, + "routes.IPSearch": 114, + "routes.CreateTopicSubmit": 115, + "routes.EditTopicSubmit": 116, + "routes.DeleteTopicSubmit": 117, + "routes.StickTopicSubmit": 118, + "routes.UnstickTopicSubmit": 119, + "routes.LockTopicSubmit": 120, + "routes.UnlockTopicSubmit": 121, + "routes.MoveTopicSubmit": 122, + "routes.LikeTopicSubmit": 123, + "routes.AddAttachToTopicSubmit": 124, + "routes.RemoveAttachFromTopicSubmit": 125, + "routes.ViewTopic": 126, + "routes.CreateReplySubmit": 127, + "routes.ReplyEditSubmit": 128, + "routes.ReplyDeleteSubmit": 129, + "routes.ReplyLikeSubmit": 130, + "routes.AddAttachToReplySubmit": 131, + "routes.RemoveAttachFromReplySubmit": 132, + "routes.ProfileReplyCreateSubmit": 133, + "routes.ProfileReplyEditSubmit": 134, + "routes.ProfileReplyDeleteSubmit": 135, + "routes.PollVote": 136, + "routes.PollResults": 137, + "routes.AccountLogin": 138, + "routes.AccountRegister": 139, + "routes.AccountLogout": 140, + "routes.AccountLoginSubmit": 141, + "routes.AccountLoginMFAVerify": 142, + "routes.AccountLoginMFAVerifySubmit": 143, + "routes.AccountRegisterSubmit": 144, + "routes.AccountPasswordReset": 145, + "routes.AccountPasswordResetSubmit": 146, + "routes.AccountPasswordResetToken": 147, + "routes.AccountPasswordResetTokenSubmit": 148, + "routes.DynamicRoute": 149, + "routes.UploadedFile": 150, + "routes.StaticFile": 151, + "routes.RobotsTxt": 152, + "routes.SitemapXml": 153, + "routes.OpenSearchXml": 154, + "routes.BadRoute": 155, + "routes.HTTPSRedirect": 156, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -429,53 +445,61 @@ var reverseRouteMapEnum = map[int]string{ 99: "routes.AccountEditEmailTokenSubmit", 100: "routes.AccountLogins", 101: "routes.LevelList", - 102: "routes.ViewProfile", - 103: "routes.BanUserSubmit", - 104: "routes.UnbanUser", - 105: "routes.ActivateUser", - 106: "routes.IPSearch", - 107: "routes.CreateTopicSubmit", - 108: "routes.EditTopicSubmit", - 109: "routes.DeleteTopicSubmit", - 110: "routes.StickTopicSubmit", - 111: "routes.UnstickTopicSubmit", - 112: "routes.LockTopicSubmit", - 113: "routes.UnlockTopicSubmit", - 114: "routes.MoveTopicSubmit", - 115: "routes.LikeTopicSubmit", - 116: "routes.AddAttachToTopicSubmit", - 117: "routes.RemoveAttachFromTopicSubmit", - 118: "routes.ViewTopic", - 119: "routes.CreateReplySubmit", - 120: "routes.ReplyEditSubmit", - 121: "routes.ReplyDeleteSubmit", - 122: "routes.ReplyLikeSubmit", - 123: "routes.AddAttachToReplySubmit", - 124: "routes.RemoveAttachFromReplySubmit", - 125: "routes.ProfileReplyCreateSubmit", - 126: "routes.ProfileReplyEditSubmit", - 127: "routes.ProfileReplyDeleteSubmit", - 128: "routes.PollVote", - 129: "routes.PollResults", - 130: "routes.AccountLogin", - 131: "routes.AccountRegister", - 132: "routes.AccountLogout", - 133: "routes.AccountLoginSubmit", - 134: "routes.AccountLoginMFAVerify", - 135: "routes.AccountLoginMFAVerifySubmit", - 136: "routes.AccountRegisterSubmit", - 137: "routes.AccountPasswordReset", - 138: "routes.AccountPasswordResetSubmit", - 139: "routes.AccountPasswordResetToken", - 140: "routes.AccountPasswordResetTokenSubmit", - 141: "routes.DynamicRoute", - 142: "routes.UploadedFile", - 143: "routes.StaticFile", - 144: "routes.RobotsTxt", - 145: "routes.SitemapXml", - 146: "routes.OpenSearchXml", - 147: "routes.BadRoute", - 148: "routes.HTTPSRedirect", + 102: "routes.Convos", + 103: "routes.ConvosCreate", + 104: "routes.Convo", + 105: "routes.ConvosCreateSubmit", + 106: "routes.ConvosDeleteSubmit", + 107: "routes.ConvosCreateReplySubmit", + 108: "routes.ConvosDeleteReplySubmit", + 109: "routes.ConvosEditReplySubmit", + 110: "routes.ViewProfile", + 111: "routes.BanUserSubmit", + 112: "routes.UnbanUser", + 113: "routes.ActivateUser", + 114: "routes.IPSearch", + 115: "routes.CreateTopicSubmit", + 116: "routes.EditTopicSubmit", + 117: "routes.DeleteTopicSubmit", + 118: "routes.StickTopicSubmit", + 119: "routes.UnstickTopicSubmit", + 120: "routes.LockTopicSubmit", + 121: "routes.UnlockTopicSubmit", + 122: "routes.MoveTopicSubmit", + 123: "routes.LikeTopicSubmit", + 124: "routes.AddAttachToTopicSubmit", + 125: "routes.RemoveAttachFromTopicSubmit", + 126: "routes.ViewTopic", + 127: "routes.CreateReplySubmit", + 128: "routes.ReplyEditSubmit", + 129: "routes.ReplyDeleteSubmit", + 130: "routes.ReplyLikeSubmit", + 131: "routes.AddAttachToReplySubmit", + 132: "routes.RemoveAttachFromReplySubmit", + 133: "routes.ProfileReplyCreateSubmit", + 134: "routes.ProfileReplyEditSubmit", + 135: "routes.ProfileReplyDeleteSubmit", + 136: "routes.PollVote", + 137: "routes.PollResults", + 138: "routes.AccountLogin", + 139: "routes.AccountRegister", + 140: "routes.AccountLogout", + 141: "routes.AccountLoginSubmit", + 142: "routes.AccountLoginMFAVerify", + 143: "routes.AccountLoginMFAVerifySubmit", + 144: "routes.AccountRegisterSubmit", + 145: "routes.AccountPasswordReset", + 146: "routes.AccountPasswordResetSubmit", + 147: "routes.AccountPasswordResetToken", + 148: "routes.AccountPasswordResetTokenSubmit", + 149: "routes.DynamicRoute", + 150: "routes.UploadedFile", + 151: "routes.StaticFile", + 152: "routes.RobotsTxt", + 153: "routes.SitemapXml", + 154: "routes.OpenSearchXml", + 155: "routes.BadRoute", + 156: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -633,7 +657,7 @@ type HTTPSRedirect struct {} func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - counters.RouteViewCounter.Bump(148) + counters.RouteViewCounter.Bump(156) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -828,6 +852,11 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { h.Set("X-Frame-Options", "deny") h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing h.Set("X-Content-Type-Options", "nosniff") + if c.Config.RefNoRef || !c.Site.EnableSsl { + h.Set("Referrer-Policy","no-referrer") + } else { + h.Set("Referrer-Policy","strict-origin") + } } if c.Dev.SuperDebug { @@ -836,8 +865,8 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Increment the request counter counters.GlobalViewCounter.Bump() - if prefix == "/static" { - counters.RouteViewCounter.Bump(143) + if prefix == "/s" { //old prefix: /static + counters.RouteViewCounter.Bump(151) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -988,14 +1017,16 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.LangViewCounter.Bump("none") } - referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P - if referrer != "" { - // ? Optimise this a little? - referrer = strings.TrimPrefix(strings.TrimPrefix(referrer,"http://"),"https://") - referrer = strings.Split(referrer,"/")[0] - portless := strings.Split(referrer,":")[0] - if portless != "localhost" && portless != "127.0.0.1" && portless != c.Site.Host { - counters.ReferrerTracker.Bump(referrer) + if !c.Config.RefNoTrack { + referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P + if referrer != "" { + // ? Optimise this a little? + referrer = strings.TrimPrefix(strings.TrimPrefix(referrer,"http://"),"https://") + referrer = strings.Split(referrer,"/")[0] + portless := strings.Split(referrer,":")[0] + if portless != "localhost" && portless != "127.0.0.1" && portless != c.Site.Host { + counters.ReferrerTracker.Bump(referrer) + } } } @@ -1813,9 +1844,110 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } err = routes.LevelList(w,req,user,head) + case "/user/convos/": + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(102) + head, err := c.UserCheck(w,req,&user) + if err != nil { + return err + } + err = routes.Convos(w,req,user,head) + case "/user/convos/create/": + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(103) + head, err := c.UserCheck(w,req,&user) + if err != nil { + return err + } + err = routes.ConvosCreate(w,req,user,head) + case "/user/convo/": + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(104) + head, err := c.UserCheck(w,req,&user) + if err != nil { + return err + } + err = routes.Convo(w,req,user,head,extraData) + case "/user/convos/create/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(105) + err = routes.ConvosCreateSubmit(w,req,user) + case "/user/convos/delete/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(106) + err = routes.ConvosDeleteSubmit(w,req,user,extraData) + case "/user/convo/create/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(107) + err = routes.ConvosCreateReplySubmit(w,req,user) + case "/user/convo/delete/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(108) + err = routes.ConvosDeleteReplySubmit(w,req,user,extraData) + case "/user/convo/edit/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(109) + err = routes.ConvosEditReplySubmit(w,req,user,extraData) default: req.URL.Path += extraData - counters.RouteViewCounter.Bump(102) + counters.RouteViewCounter.Bump(110) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1835,7 +1967,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(103) + counters.RouteViewCounter.Bump(111) err = routes.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = c.NoSessionMismatch(w,req,user) @@ -1848,7 +1980,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(104) + counters.RouteViewCounter.Bump(112) err = routes.UnbanUser(w,req,user,extraData) case "/users/activate/": err = c.NoSessionMismatch(w,req,user) @@ -1861,7 +1993,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(105) + counters.RouteViewCounter.Bump(113) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = c.MemberOnly(w,req,user) @@ -1869,7 +2001,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(106) + counters.RouteViewCounter.Bump(114) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1893,7 +2025,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(107) + counters.RouteViewCounter.Bump(115) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1906,7 +2038,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(108) + counters.RouteViewCounter.Bump(116) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1920,7 +2052,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(109) + counters.RouteViewCounter.Bump(117) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1933,7 +2065,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(110) + counters.RouteViewCounter.Bump(118) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1946,7 +2078,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(111) + counters.RouteViewCounter.Bump(119) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1960,7 +2092,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(112) + counters.RouteViewCounter.Bump(120) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1973,7 +2105,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(113) + counters.RouteViewCounter.Bump(121) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1986,7 +2118,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(114) + counters.RouteViewCounter.Bump(122) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1999,7 +2131,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(115) + counters.RouteViewCounter.Bump(123) err = routes.LikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2016,7 +2148,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(116) + counters.RouteViewCounter.Bump(124) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2029,10 +2161,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(117) + counters.RouteViewCounter.Bump(125) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - counters.RouteViewCounter.Bump(118) + counters.RouteViewCounter.Bump(126) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2056,7 +2188,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(119) + counters.RouteViewCounter.Bump(127) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2069,7 +2201,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(120) + counters.RouteViewCounter.Bump(128) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2082,7 +2214,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(121) + counters.RouteViewCounter.Bump(129) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2095,7 +2227,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(122) + counters.RouteViewCounter.Bump(130) err = routes.ReplyLikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2112,7 +2244,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(123) + counters.RouteViewCounter.Bump(131) err = routes.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2125,7 +2257,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(124) + counters.RouteViewCounter.Bump(132) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2141,7 +2273,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(125) + counters.RouteViewCounter.Bump(133) err = routes.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2154,7 +2286,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(126) + counters.RouteViewCounter.Bump(134) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2167,7 +2299,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(127) + counters.RouteViewCounter.Bump(135) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2183,23 +2315,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(128) + counters.RouteViewCounter.Bump(136) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - counters.RouteViewCounter.Bump(129) + counters.RouteViewCounter.Bump(137) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - counters.RouteViewCounter.Bump(130) + counters.RouteViewCounter.Bump(138) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountLogin(w,req,user,head) case "/accounts/create/": - counters.RouteViewCounter.Bump(131) + counters.RouteViewCounter.Bump(139) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2216,7 +2348,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(132) + counters.RouteViewCounter.Bump(140) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2224,10 +2356,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(133) + counters.RouteViewCounter.Bump(141) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - counters.RouteViewCounter.Bump(134) + counters.RouteViewCounter.Bump(142) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2239,7 +2371,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(135) + counters.RouteViewCounter.Bump(143) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2247,10 +2379,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(136) + counters.RouteViewCounter.Bump(144) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - counters.RouteViewCounter.Bump(137) + counters.RouteViewCounter.Bump(145) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2262,10 +2394,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(138) + counters.RouteViewCounter.Bump(146) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - counters.RouteViewCounter.Bump(139) + counters.RouteViewCounter.Bump(147) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2277,7 +2409,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(140) + counters.RouteViewCounter.Bump(148) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2294,7 +2426,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c h.Del("Content-Type") h.Del("Content-Encoding") } - counters.RouteViewCounter.Bump(142) + counters.RouteViewCounter.Bump(150) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2304,7 +2436,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c // TODO: Add support for favicons and robots.txt files switch(extraData) { case "robots.txt": - counters.RouteViewCounter.Bump(144) + counters.RouteViewCounter.Bump(152) return routes.RobotsTxt(w,req) case "favicon.ico": gzw, ok := w.(c.GzipResponseWriter) @@ -2314,14 +2446,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c h.Del("Content-Type") h.Del("Content-Encoding") } - req.URL.Path = "/static/favicon.ico" + req.URL.Path = "/s/favicon.ico" routes.StaticFile(w,req) return nil case "opensearch.xml": - counters.RouteViewCounter.Bump(146) + counters.RouteViewCounter.Bump(154) return routes.OpenSearchXml(w,req) /*case "sitemap.xml": - counters.RouteViewCounter.Bump(145) + counters.RouteViewCounter.Bump(153) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2332,7 +2464,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - counters.RouteViewCounter.Bump(141) // TODO: Be more specific about *which* dynamic route it is + counters.RouteViewCounter.Bump(149) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2343,7 +2475,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - counters.RouteViewCounter.Bump(147) + counters.RouteViewCounter.Bump(155) return c.NotFound(w,req,nil) } return err diff --git a/general_test.go b/general_test.go index 7682e87f..e9fc6419 100644 --- a/general_test.go +++ b/general_test.go @@ -567,7 +567,7 @@ func BenchmarkRoutesSerial(b *testing.B) { b.Log("Plugins have already been initialised, they can't be deinitialised so these tests will run with plugins on") } static_w := httptest.NewRecorder() - static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil)) + static_req := httptest.NewRequest("get","/s/global.js",bytes.NewReader(nil)) static_handler := http.HandlerFunc(route_static) topic_w := httptest.NewRecorder() @@ -1134,7 +1134,7 @@ func TestStaticRoute(t *testing.T) { } static_w := httptest.NewRecorder() - static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil)) + static_req := httptest.NewRequest("get","/s/global.js",bytes.NewReader(nil)) static_handler := http.HandlerFunc(route_static) static_handler.ServeHTTP(static_w,static_req) diff --git a/langs/english.json b/langs/english.json index 65a279e4..23d9dc2e 100644 --- a/langs/english.json +++ b/langs/english.json @@ -318,6 +318,8 @@ "password_reset_email_sent":"An email was sent to you. Please follow the steps within.", "password_reset_token_token_verified":"Your password was successfully updated.", + "convo_dev":"Messages are currently under development. Some features may not work yet and your messages may be purged every now and then.", + "panel_forum_created":"The forum was successfully created.", "panel_forum_deleted":"The forum was successfully deleted.", "panel_forum_updated":"The forum was successfully updated.", diff --git a/main.go b/main.go index 25d2c376..121a3aa6 100644 --- a/main.go +++ b/main.go @@ -134,10 +134,10 @@ func storeInit() (err error) { if err != nil { return errors.WithStack(err) } - /*c.Convos, err = c.NewDefaultConversationStore(acc) + c.Convos, err = c.NewDefaultConversationStore(acc) if err != nil { return errors.WithStack(err) - }*/ + } err = phrases.InitPhrases(c.Site.Language) if err != nil { diff --git a/patcher/patches.go b/patcher/patches.go index 187a1e9f..1c81b4df 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -9,6 +9,7 @@ import ( ) type tblColumn = qgen.DBTableColumn +type tC = tblColumn type tblKey = qgen.DBTableKey func init() { @@ -35,6 +36,7 @@ func init() { addPatch(20, patch20) addPatch(21, patch21) addPatch(22, patch22) + addPatch(23, patch23) } func patch0(scanner *bufio.Scanner) (err error) { @@ -48,8 +50,8 @@ func patch0(scanner *bufio.Scanner) (err error) { } err = execStmt(qgen.Builder.CreateTable("menus", "", "", - []tblColumn{ - tblColumn{"mid", "int", 0, false, true, ""}, + []tC{ + tC{"mid", "int", 0, false, true, ""}, }, []tblKey{ tblKey{"mid", "primary","",false}, @@ -60,23 +62,23 @@ func patch0(scanner *bufio.Scanner) (err error) { } err = execStmt(qgen.Builder.CreateTable("menu_items", "", "", - []tblColumn{ - tblColumn{"miid", "int", 0, false, true, ""}, - tblColumn{"mid", "int", 0, false, false, ""}, - tblColumn{"name", "varchar", 200, false, false, ""}, - tblColumn{"htmlID", "varchar", 200, false, false, "''"}, - tblColumn{"cssClass", "varchar", 200, false, false, "''"}, - tblColumn{"position", "varchar", 100, false, false, ""}, - tblColumn{"path", "varchar", 200, false, false, "''"}, - tblColumn{"aria", "varchar", 200, false, false, "''"}, - tblColumn{"tooltip", "varchar", 200, false, false, "''"}, - tblColumn{"tmplName", "varchar", 200, false, false, "''"}, - tblColumn{"order", "int", 0, false, false, "0"}, + []tC{ + tC{"miid", "int", 0, false, true, ""}, + tC{"mid", "int", 0, false, false, ""}, + tC{"name", "varchar", 200, false, false, ""}, + tC{"htmlID", "varchar", 200, false, false, "''"}, + tC{"cssClass", "varchar", 200, false, false, "''"}, + tC{"position", "varchar", 100, false, false, ""}, + tC{"path", "varchar", 200, false, false, "''"}, + tC{"aria", "varchar", 200, false, false, "''"}, + tC{"tooltip", "varchar", 200, false, false, "''"}, + tC{"tmplName", "varchar", 200, false, false, "''"}, + tC{"order", "int", 0, false, false, "0"}, - tblColumn{"guestOnly", "boolean", 0, false, false, "0"}, - tblColumn{"memberOnly", "boolean", 0, false, false, "0"}, - tblColumn{"staffOnly", "boolean", 0, false, false, "0"}, - tblColumn{"adminOnly", "boolean", 0, false, false, "0"}, + tC{"guestOnly", "boolean", 0, false, false, "0"}, + tC{"memberOnly", "boolean", 0, false, false, "0"}, + tC{"staffOnly", "boolean", 0, false, false, "0"}, + tC{"adminOnly", "boolean", 0, false, false, "0"}, }, []tblKey{ tblKey{"miid", "primary","",false}, @@ -176,14 +178,14 @@ func patch2(scanner *bufio.Scanner) error { func patch3(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.CreateTable("registration_logs", "", "", - []tblColumn{ - tblColumn{"rlid", "int", 0, false, true, ""}, - tblColumn{"username", "varchar", 100, false, false, ""}, - tblColumn{"email", "varchar", 100, false, false, ""}, - tblColumn{"failureReason", "varchar", 100, false, false, ""}, - tblColumn{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? - tblColumn{"ipaddress", "varchar", 200, false, false, ""}, - tblColumn{"doneAt", "createdAt", 0, false, false, ""}, + []tC{ + tC{"rlid", "int", 0, false, true, ""}, + tC{"username", "varchar", 100, false, false, ""}, + tC{"email", "varchar", 100, false, false, ""}, + tC{"failureReason", "varchar", 100, false, false, ""}, + tC{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? + tC{"ipaddress", "varchar", 200, false, false, ""}, + tC{"doneAt", "createdAt", 0, false, false, ""}, }, []tblKey{ tblKey{"rlid", "primary","",false}, @@ -240,13 +242,13 @@ func patch4(scanner *bufio.Scanner) error { } err = execStmt(qgen.Builder.CreateTable("pages", "utf8mb4", "utf8mb4_general_ci", - []tblColumn{ - tblColumn{"pid", "int", 0, false, true, ""}, - tblColumn{"name", "varchar", 200, false, false, ""}, - tblColumn{"title", "varchar", 200, false, false, ""}, - tblColumn{"body", "text", 0, false, false, ""}, - tblColumn{"allowedGroups", "text", 0, false, false, ""}, - tblColumn{"menuID", "int", 0, false, false, "-1"}, + []tC{ + tC{"pid", "int", 0, false, true, ""}, + tC{"name", "varchar", 200, false, false, ""}, + tC{"title", "varchar", 200, false, false, ""}, + tC{"body", "text", 0, false, false, ""}, + tC{"allowedGroups", "text", 0, false, false, ""}, + tC{"menuID", "int", 0, false, false, "-1"}, }, []tblKey{ tblKey{"pid", "primary","",false}, @@ -278,18 +280,18 @@ func patch5(scanner *bufio.Scanner) error { } err = execStmt(qgen.Builder.CreateTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci", - []tblColumn{ - tblColumn{"uid", "int", 0, false, false, ""}, - tblColumn{"secret", "varchar", 100, false, false, ""}, - tblColumn{"scratch1", "varchar", 50, false, false, ""}, - tblColumn{"scratch2", "varchar", 50, false, false, ""}, - tblColumn{"scratch3", "varchar", 50, false, false, ""}, - tblColumn{"scratch4", "varchar", 50, false, false, ""}, - tblColumn{"scratch5", "varchar", 50, false, false, ""}, - tblColumn{"scratch6", "varchar", 50, false, false, ""}, - tblColumn{"scratch7", "varchar", 50, false, false, ""}, - tblColumn{"scratch8", "varchar", 50, false, false, ""}, - tblColumn{"createdAt", "createdAt", 0, false, false, ""}, + []tC{ + tC{"uid", "int", 0, false, false, ""}, + tC{"secret", "varchar", 100, false, false, ""}, + tC{"scratch1", "varchar", 50, false, false, ""}, + tC{"scratch2", "varchar", 50, false, false, ""}, + tC{"scratch3", "varchar", 50, false, false, ""}, + tC{"scratch4", "varchar", 50, false, false, ""}, + tC{"scratch5", "varchar", 50, false, false, ""}, + tC{"scratch6", "varchar", 50, false, false, ""}, + tC{"scratch7", "varchar", 50, false, false, ""}, + tC{"scratch8", "varchar", 50, false, false, ""}, + tC{"createdAt", "createdAt", 0, false, false, ""}, }, []tblKey{ tblKey{"uid", "primary","",false}, @@ -308,8 +310,8 @@ func patch6(scanner *bufio.Scanner) error { func patch7(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.CreateTable("users_avatar_queue", "", "", - []tblColumn{ - tblColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + []tC{ + tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key }, []tblKey{ tblKey{"uid", "primary","",false}, @@ -371,8 +373,8 @@ func patch8(scanner *bufio.Scanner) error { return err } return execStmt(qgen.Builder.CreateTable("updates", "", "", - []tblColumn{ - tblColumn{"dbVersion", "int", 0, false, false, "0"}, + []tC{ + tC{"dbVersion", "int", 0, false, false, "0"}, }, []tblKey{}, )) @@ -386,12 +388,12 @@ func patch9(scanner *bufio.Scanner) error { } return execStmt(qgen.Builder.CreateTable("login_logs", "", "", - []tblColumn{ - tblColumn{"lid", "int", 0, false, true, ""}, - tblColumn{"uid", "int", 0, false, false, ""}, - tblColumn{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? - tblColumn{"ipaddress", "varchar", 200, false, false, ""}, - tblColumn{"doneAt", "createdAt", 0, false, false, ""}, + []tC{ + tC{"lid", "int", 0, false, true, ""}, + tC{"uid", "int", 0, false, false, ""}, + tC{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? + tC{"ipaddress", "varchar", 200, false, false, ""}, + tC{"doneAt", "createdAt", 0, false, false, ""}, }, []tblKey{ tblKey{"lid", "primary","",false}, @@ -403,11 +405,11 @@ var acc = qgen.NewAcc var itoa = strconv.Itoa func patch10(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("topics", tblColumn{"attachCount", "int", 0, false, false, "0"}, nil)) + err := execStmt(qgen.Builder.AddColumn("topics", tC{"attachCount", "int", 0, false, false, "0"}, nil)) if err != nil { return err } - err = execStmt(qgen.Builder.AddColumn("topics", tblColumn{"lastReplyID", "int", 0, false, false, "0"}, nil)) + err = execStmt(qgen.Builder.AddColumn("topics", tC{"lastReplyID", "int", 0, false, false, "0"}, nil)) if err != nil { return err } @@ -443,7 +445,7 @@ func patch10(scanner *bufio.Scanner) error { } func patch11(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("replies", tblColumn{"attachCount", "int", 0, false, false, "0"}, nil)) + err := execStmt(qgen.Builder.AddColumn("replies", tC{"attachCount", "int", 0, false, false, "0"}, nil)) if err != nil { return err } @@ -517,7 +519,7 @@ func patch12(scanner *bufio.Scanner) error { } func patch13(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("widgets", tblColumn{"wid", "int", 0, false, true, ""}, &tblKey{"wid", "primary","",false})) + err := execStmt(qgen.Builder.AddColumn("widgets", tC{"wid", "int", 0, false, true, ""}, &tblKey{"wid", "primary","",false})) if err != nil { return err } @@ -548,18 +550,18 @@ func patch15(scanner *bufio.Scanner) error { func patch16(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.CreateTable("password_resets", "", "", - []tblColumn{ - tblColumn{"email", "varchar", 200, false, false, ""}, - tblColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tblColumn{"validated", "varchar", 200, false, false, ""}, // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token - tblColumn{"token", "varchar", 200, false, false, ""}, - tblColumn{"createdAt", "createdAt", 0, false, false, ""}, + []tC{ + tC{"email", "varchar", 200, false, false, ""}, + tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + tC{"validated", "varchar", 200, false, false, ""}, // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token + tC{"token", "varchar", 200, false, false, ""}, + tC{"createdAt", "createdAt", 0, false, false, ""}, }, nil, )) } func patch17(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("attachments", tblColumn{"extra", "varchar", 200, false, false, ""}, nil)) + err := execStmt(qgen.Builder.AddColumn("attachments", tC{"extra", "varchar", 200, false, false, ""}, nil)) if err != nil { return err } @@ -595,14 +597,14 @@ func patch17(scanner *bufio.Scanner) error { } func patch18(scanner *bufio.Scanner) error { - return execStmt(qgen.Builder.AddColumn("forums", tblColumn{"order", "int", 0, false, false, "0"}, nil)) + return execStmt(qgen.Builder.AddColumn("forums", tC{"order", "int", 0, false, false, "0"}, nil)) } func patch19(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.CreateTable("memchunks", "", "", - []tblColumn{ - tblColumn{"count", "int", 0, false, false, "0"}, - tblColumn{"createdAt", "datetime", 0, false, false, ""}, + []tC{ + tC{"count", "int", 0, false, false, "0"}, + tC{"createdAt", "datetime", 0, false, false, ""}, }, nil, )) } @@ -631,29 +633,82 @@ func patch20(scanner *bufio.Scanner) error { } func patch21(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("memchunks", tblColumn{"stack", "int", 0, false, false, "0"}, nil)) + err := execStmt(qgen.Builder.AddColumn("memchunks", tC{"stack", "int", 0, false, false, "0"}, nil)) if err != nil { return err } - err = execStmt(qgen.Builder.AddColumn("memchunks", tblColumn{"heap", "int", 0, false, false, "0"}, nil)) + err = execStmt(qgen.Builder.AddColumn("memchunks", tC{"heap", "int", 0, false, false, "0"}, nil)) if err != nil { return err } err = execStmt(qgen.Builder.CreateTable("meta", "", "", - []tblColumn{ - tblColumn{"name", "varchar", 200, false, false, ""}, - tblColumn{"value", "varchar", 200, false, false, ""}, + []tC{ + tC{"name", "varchar", 200, false, false, ""}, + tC{"value", "varchar", 200, false, false, ""}, }, nil, )) if err != nil { return err } - return execStmt(qgen.Builder.AddColumn("activity_stream", tblColumn{"createdAt", "createdAt", 0, false, false, ""}, nil)) + return execStmt(qgen.Builder.AddColumn("activity_stream", tC{"createdAt", "createdAt", 0, false, false, ""}, nil)) } func patch22(scanner *bufio.Scanner) error { - return execStmt(qgen.Builder.AddColumn("forums", tblColumn{"tmpl", "varchar", 200, false, false, "''"}, nil)) + return execStmt(qgen.Builder.AddColumn("forums", tC{"tmpl", "varchar", 200, false, false, "''"}, nil)) +} + +func patch23(scanner *bufio.Scanner) error { + err := execStmt(qgen.Builder.DropTable("conversations")) + if err != nil { + return err + } + err = execStmt(qgen.Builder.CreateTable("conversations", "", "", + []tC{ + tC{"cid", "int", 0, false, true, ""}, + tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key + tC{"createdAt", "createdAt", 0, false, false, ""}, + tC{"lastReplyAt", "datetime", 0, false, false, ""}, + tC{"lastReplyBy", "int", 0, false, false, ""}, + }, + []tblKey{ + tblKey{"cid", "primary","",false}, + }, + )) + if err != nil { + return err + } + + err = execStmt(qgen.Builder.DropTable("conversations_posts")) + if err != nil { + return err + } + err = execStmt(qgen.Builder.CreateTable("conversations_posts", "", "", + []tC{ + tC{"pid", "int", 0, false, true, ""}, + tC{"cid", "int", 0, false, false, ""}, + tC{"createdBy", "int", 0, false, false, ""}, + tC{"body", "varchar", 50, false, false, ""}, + tC{"post", "varchar", 50, false, false, "''"}, + }, + []tblKey{ + tblKey{"pid", "primary","",false}, + }, + )) + if err != nil { + return err + } + + err = execStmt(qgen.Builder.DropTable("conversations_participants")) + if err != nil { + return err + } + return execStmt(qgen.Builder.CreateTable("conversations_participants", "", "", + []tC{ + tC{"uid", "int", 0, false, false, ""}, + tC{"cid", "int", 0, false, false, ""}, + }, nil, + )) } \ No newline at end of file diff --git a/public/init.js b/public/init.js index c5517920..44777ced 100644 --- a/public/init.js +++ b/public/init.js @@ -83,9 +83,9 @@ function asyncGetScript(source) { } function notifyOnScript(source) { - source = "/static/"+source; + source = "/s/"+source; return new Promise((resolve, reject) => { - let ss = source.replace("/static/",""); + let ss = source.replace("/s/",""); try { let ssp = ss.charAt(0).toUpperCase() + ss.slice(1) console.log("ssp:",ssp) @@ -138,8 +138,8 @@ function loadScript(name, callback,fail) { let parts = value.split("; current_theme="); if (parts.length == 2) fname += "_"+ parts.pop().split(";").shift(); - let url = "/static/"+fname+".js" - let iurl = "/static/"+name+".js" + let url = "/s/"+fname+".js" + let iurl = "/s/"+name+".js" asyncGetScript(url) .then(callback) .catch((e) => { @@ -161,7 +161,7 @@ function loadScript(name, callback,fail) { /* function loadTmpl(name,callback) { - let url = "/static/"+name + let url = "/s/"+name let worker = new Worker(url); } */ diff --git a/public/jquery-emojiarea/emojis.js b/public/jquery-emojiarea/emojis.js index ff684dd4..c94a7c58 100644 --- a/public/jquery-emojiarea/emojis.js +++ b/public/jquery-emojiarea/emojis.js @@ -1,4 +1,4 @@ -$.emojiarea.path = '/static/smilies/emojiarea'; +$.emojiarea.path = '/s/smilies/emojiarea'; $.emojiarea.icons = [{"name" : "", "icons" : {':bowtie:' : 'bowtie.png', ':smile:' : 'smile.png', ':laughing:' : 'laughing.png', diff --git a/query_gen/mysql.go b/query_gen/mysql.go index 17dd84ce..8d99e7cc 100644 --- a/query_gen/mysql.go +++ b/query_gen/mysql.go @@ -654,7 +654,7 @@ func (a *MysqlAdapter) complexSelect(preBuilder *selectPrebuilder, sb *strings.B return nil } -func (adapter *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { +func (a *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { if table1 == "" { return "", errors.New("You need a name for the left table") } @@ -668,7 +668,7 @@ func (adapter *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 s return "", errors.New("No joiners found for SimpleLeftJoin") } - whereStr, err := adapter.buildJoinWhere(where) + whereStr, err := a.buildJoinWhere(where) if err != nil { return "", err } @@ -684,14 +684,14 @@ func (adapter *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 s as2 = " AS `"+ thalf2[1]+"`" } - var querystr = "SELECT" + adapter.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`"+as1+" LEFT JOIN `" + thalf2[0] + "`"+as2+" ON " + adapter.buildJoiners(joiners) + whereStr + adapter.buildOrderby(orderby) + adapter.buildLimit(limit) + q := "SELECT" + a.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`"+as1+" LEFT JOIN `" + thalf2[0] + "`"+as2+" ON " + a.buildJoiners(joiners) + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) - querystr = strings.TrimSpace(querystr) - adapter.pushStatement(name, "select", querystr) - return querystr, nil + q = strings.TrimSpace(q) + a.pushStatement(name, "select", q) + return q, nil } -func (adapter *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { +func (a *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { if table1 == "" { return "", errors.New("You need a name for the left table") } @@ -705,16 +705,27 @@ func (adapter *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2 return "", errors.New("No joiners found for SimpleInnerJoin") } - whereStr, err := adapter.buildJoinWhere(where) + whereStr, err := a.buildJoinWhere(where) if err != nil { return "", err } - var querystr = "SELECT " + adapter.buildJoinColumns(columns) + " FROM `" + table1 + "` INNER JOIN `" + table2 + "` ON " + adapter.buildJoiners(joiners) + whereStr + adapter.buildOrderby(orderby) + adapter.buildLimit(limit) + thalf1 := strings.Split(strings.Replace(table1," as ", " AS ",-1)," AS ") + var as1 string + if len(thalf1) == 2 { + as1 = " AS `"+ thalf1[1]+"`" + } + thalf2 := strings.Split(strings.Replace(table2," as ", " AS ",-1)," AS ") + var as2 string + if len(thalf2) == 2 { + as2 = " AS `"+ thalf2[1]+"`" + } - querystr = strings.TrimSpace(querystr) - adapter.pushStatement(name, "select", querystr) - return querystr, nil + q := "SELECT " + a.buildJoinColumns(columns) + " FROM `" + thalf1[0] + "`"+as1+" INNER JOIN `" + thalf2[0] + "`"+as2+" ON " + a.buildJoiners(joiners) + whereStr + a.buildOrderby(orderby) + a.buildLimit(limit) + + q = strings.TrimSpace(q) + a.pushStatement(name, "select", q) + return q, nil } func (adapter *MysqlAdapter) SimpleUpdateSelect(up *updatePrebuilder) (string, error) { @@ -767,7 +778,7 @@ func (adapter *MysqlAdapter) SimpleInsertLeftJoin(name string, ins DBInsert, sel return "", err } - var q = "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) + q := "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) q = strings.TrimSpace(q) adapter.pushStatement(name, "insert", q) @@ -856,7 +867,7 @@ func (adapter *MysqlAdapter) SimpleInsertInnerJoin(name string, ins DBInsert, se return "", err } - var q = "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) + q := "INSERT INTO `" + ins.Table + "`(" + adapter.buildColumns(ins.Columns) + ") SELECT" + adapter.buildJoinColumns(sel.Columns) + " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " + adapter.buildJoiners(sel.Joiners) + whereStr + adapter.buildOrderby(sel.Orderby) + adapter.buildLimit(sel.Limit) q = strings.TrimSpace(q) adapter.pushStatement(name, "insert", q) @@ -882,13 +893,13 @@ func (adapter *MysqlAdapter) Builder() *prebuilder { return &prebuilder{adapter} } -func (adapter *MysqlAdapter) Write() error { +func (a *MysqlAdapter) Write() error { var stmts, body string - for _, name := range adapter.BufferOrder { + for _, name := range a.BufferOrder { if name[0] == '_' { continue } - stmt := adapter.Buffer[name] + stmt := a.Buffer[name] // ? - Table creation might be a little complex for Go to do outside a SQL file :( if stmt.Type == "upsert" { stmts += "\t" + name + " *qgen.MySQLUpsertCallback\n" @@ -945,12 +956,12 @@ func _gen_mysql() (err error) { } // Internal methods, not exposed in the interface -func (adapter *MysqlAdapter) pushStatement(name string, stype string, querystr string) { +func (a *MysqlAdapter) pushStatement(name string, stype string, querystr string) { if name == "" { return } - adapter.Buffer[name] = DBStmt{querystr, stype} - adapter.BufferOrder = append(adapter.BufferOrder, name) + a.Buffer[name] = DBStmt{querystr, stype} + a.BufferOrder = append(a.BufferOrder, name) } func (adapter *MysqlAdapter) stringyType(ctype string) bool { diff --git a/router_gen/main.go b/router_gen/main.go index f012a495..13f0be1f 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -593,6 +593,11 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { h.Set("X-Frame-Options", "deny") h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing h.Set("X-Content-Type-Options", "nosniff") + if c.Config.RefNoRef || !c.Site.EnableSsl { + h.Set("Referrer-Policy","no-referrer") + } else { + h.Set("Referrer-Policy","strict-origin") + } } if c.Dev.SuperDebug { @@ -601,7 +606,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Increment the request counter counters.GlobalViewCounter.Bump() - if prefix == "/static" { + if prefix == "/s" { //old prefix: /static counters.RouteViewCounter.Bump({{ index .AllRouteMap "routes.StaticFile" }}) req.URL.Path += extraData routes.StaticFile(w, req) @@ -753,14 +758,16 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.LangViewCounter.Bump("none") } - referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P - if referrer != "" { - // ? Optimise this a little? - referrer = strings.TrimPrefix(strings.TrimPrefix(referrer,"http://"),"https://") - referrer = strings.Split(referrer,"/")[0] - portless := strings.Split(referrer,":")[0] - if portless != "localhost" && portless != "127.0.0.1" && portless != c.Site.Host { - counters.ReferrerTracker.Bump(referrer) + if !c.Config.RefNoTrack { + referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P + if referrer != "" { + // ? Optimise this a little? + referrer = strings.TrimPrefix(strings.TrimPrefix(referrer,"http://"),"https://") + referrer = strings.Split(referrer,"/")[0] + portless := strings.Split(referrer,":")[0] + if portless != "localhost" && portless != "127.0.0.1" && portless != c.Site.Host { + counters.ReferrerTracker.Bump(referrer) + } } } @@ -840,7 +847,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c h.Del("Content-Type") h.Del("Content-Encoding") } - req.URL.Path = "/static/favicon.ico" + req.URL.Path = "/s/favicon.ico" routes.StaticFile(w,req) return nil case "opensearch.xml": diff --git a/router_gen/routes.go b/router_gen/routes.go index c15e0c0e..419b435f 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -49,7 +49,7 @@ func userRoutes() *RouteGroup { return newRouteGroup("/user/").Routes( View("routes.ViewProfile", "/user/").LitBefore("req.URL.Path += extraData"), - Set("routes.AccountEdit","/user/edit", + Set("routes.AccountEdit", "/user/edit/", MView("", "/"), MView("Password", "/password/"), Action("PasswordSubmit", "/password/submit/"), // TODO: Full test this @@ -82,14 +82,15 @@ func userRoutes() *RouteGroup { MView("routes.LevelList", "/user/levels/"), //MView("routes.LevelRankings", "/user/rankings/"), //MView("routes.Alerts", "/user/alerts/"), - - /*MView("routes.Convos", "/user/convos/"), + + MView("routes.Convos", "/user/convos/"), MView("routes.ConvosCreate", "/user/convos/create/"), - MView("routes.Convo", "/user/convo/","extraData"), + MView("routes.Convo", "/user/convo/", "extraData"), Action("routes.ConvosCreateSubmit", "/user/convos/create/submit/"), - Action("routes.ConvosDeleteSubmit", "/user/convos/delete/submit/","extraData"), + Action("routes.ConvosDeleteSubmit", "/user/convos/delete/submit/", "extraData"), Action("routes.ConvosCreateReplySubmit", "/user/convo/create/submit/"), - Action("routes.ConvosDeleteReplySubmit", "/user/convo/delete/submit/"),*/ + Action("routes.ConvosDeleteReplySubmit", "/user/convo/delete/submit/","extraData"), + Action("routes.ConvosEditReplySubmit", "/user/convo/edit/submit/", "extraData"), ) } @@ -171,6 +172,8 @@ func accountRoutes() *RouteGroup { func panelRoutes() *RouteGroup { return newRouteGroup("/panel/").Before("SuperModOnly").NoHeader().Routes( View("panel.Dashboard", "/panel/"), + //View("panel.StatsDisk", "/panel/stats/disk/"), + View("panel.Forums", "/panel/forums/"), Action("panel.ForumsCreateSubmit", "/panel/forums/create/"), Action("panel.ForumsDelete", "/panel/forums/delete/", "extraData"), diff --git a/routes/common.go b/routes/common.go index 07caa2ea..062c4faf 100644 --- a/routes/common.go +++ b/routes/common.go @@ -21,8 +21,8 @@ func ParseSEOURL(urlBit string) (slug string, id int, err error) { return halves[0], tid, err } -var slen1 = len("; rel=preload; as=script,") -var slen2 = len("; rel=preload; as=style,") +var slen1 = len("; rel=preload; as=script,") +var slen2 = len("; rel=preload; as=style,") func doPush(w http.ResponseWriter, header *c.Header) { //fmt.Println("in doPush") @@ -32,7 +32,7 @@ func doPush(w http.ResponseWriter, header *c.Header) { var push = func(in []string) { sb.Grow((slen1 + 5) * len(in)) for _, path := range in { - sb.WriteString("; rel=preload; as=script,") } @@ -44,7 +44,7 @@ func doPush(w http.ResponseWriter, header *c.Header) { if len(header.Stylesheets) > 0 { sb.Grow((slen2 + 6) * len(header.Stylesheets)) for _, path := range header.Stylesheets { - sb.WriteString("; rel=preload; as=style,") } @@ -69,9 +69,9 @@ func doPush(w http.ResponseWriter, header *c.Header) { var push = func(in []string) { for _, path := range in { - //fmt.Println("pushing /static/" + path) + //fmt.Println("pushing /s/" + path) // TODO: Avoid concatenating here - err := pusher.Push("/static/"+path, nil) + err := pusher.Push("/s/"+path, nil) if err != nil { break } @@ -116,7 +116,7 @@ func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r h.Stylesheets = nil c.PrepResources(&h.CurrentUser, h, h.Theme) for _, ss := range s { - h.Stylesheets = append(h.Stylesheets,ss) + h.Stylesheets = append(h.Stylesheets, ss) } if h.CurrentUser.Loggedin { @@ -141,4 +141,4 @@ func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r } // TODO: Rename renderTemplate to RenderTemplate instead of using this hack to avoid breaking things -var RenderTemplate = renderTemplate3 \ No newline at end of file +var RenderTemplate = renderTemplate3 diff --git a/routes/convos.go b/routes/convos.go index a1781c76..698049ee 100644 --- a/routes/convos.go +++ b/routes/convos.go @@ -7,11 +7,12 @@ import ( "strings" c "github.com/Azareal/Gosora/common" - "github.com/Azareal/Gosora/common/phrases" + p "github.com/Azareal/Gosora/common/phrases" ) func Convos(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { accountEditHead("convos", w, r, &user, header) + header.AddNotice("convo_dev") ccount := c.Convos.GetUserCount(user.ID) page, _ := strconv.Atoi(r.FormValue("page")) offset, page, lastPage := c.PageOffset(ccount, page, c.Config.ItemsPerPage) @@ -30,9 +31,11 @@ func Convos(w http.ResponseWriter, r *http.Request, user c.User, header *c.Heade func Convo(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header, scid string) c.RouteError { accountEditHead("convo", w, r, &user, header) + header.AddSheet(header.Theme.Name + "/convo.css") + header.AddNotice("convo_dev") cid, err := strconv.Atoi(scid) if err != nil { - return c.LocalError(phrases.GetErrorPhrase("id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) } convo, err := c.Convos.Get(cid) @@ -50,7 +53,7 @@ func Convo(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header offset, page, lastPage := c.PageOffset(pcount, page, c.Config.ItemsPerPage) pageList := c.Paginate(page, lastPage, 5) - posts, err := convo.Posts(offset) + posts, err := convo.Posts(offset, c.Config.ItemsPerPage) // TODO: Report a better error for no posts if err == sql.ErrNoRows { return c.NotFound(w, r, header) @@ -58,12 +61,23 @@ func Convo(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header return c.InternalError(err, w, r) } - pi := c.Account{header, "dashboard", "convo", c.ConvoViewPage{header, posts, c.Paginator{pageList, page, lastPage}}} + pitems := make([]c.ConvoViewRow, len(posts)) + for i, post := range posts { + user, err := c.Users.Get(post.CreatedBy) + if err != nil { + return c.InternalError(err, w, r) + } + pitems[i] = c.ConvoViewRow{post, user, "", "4"} + } + canModify := user.ID == convo.CreatedBy || user.IsSuperMod + + pi := c.Account{header, "dashboard", "convo", c.ConvoViewPage{header, convo, pitems, canModify, c.Paginator{pageList, page, lastPage}}} return renderTemplate("account", w, r, header, pi) } func ConvosCreate(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { accountEditHead("create_convo", w, r, &user, header) + header.AddNotice("convo_dev") recpName := "" pi := c.Account{header, "dashboard", "create_convo", c.ConvoCreatePage{header, recpName}} return renderTemplate("account", w, r, header, pi) @@ -103,13 +117,6 @@ func ConvosCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.R return nil } -/*type ConversationPost struct { - ID int - CID int - Body string - Post string // aes, '' -}*/ - func ConvosDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, scid string) c.RouteError { _, ferr := c.SimpleUserCheck(w, r, &user) if ferr != nil { @@ -117,7 +124,7 @@ func ConvosDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, sci } cid, err := strconv.Atoi(scid) if err != nil { - return c.LocalError(phrases.GetErrorPhrase("id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) } err = c.Convos.Delete(cid) @@ -138,11 +145,61 @@ func ConvosCreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User return nil } -func ConvosDeleteReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { +func ConvosDeleteReplySubmit(w http.ResponseWriter, r *http.Request, user c.User, scpid string) c.RouteError { + _, ferr := c.SimpleUserCheck(w, r, &user) + if ferr != nil { + return ferr + } + cpid, err := strconv.Atoi(scpid) + if err != nil { + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) + } + + post := &c.ConversationPost{ID: cpid} + err = post.Fetch() + if err == sql.ErrNoRows { + return c.NotFound(w, r, nil) + } else if err != nil { + return c.InternalError(err, w, r) + } + + convo, err := c.Convos.Get(post.CID) + if err == sql.ErrNoRows { + return c.NotFound(w, r, nil) + } else if err != nil { + return c.InternalError(err, w, r) + } + pcount := convo.PostsCount() + if pcount == 0 { + return c.NotFound(w, r, nil) + } + + posts, err := convo.Posts(0, c.Config.ItemsPerPage) + // TODO: Report a better error for no posts + if err == sql.ErrNoRows { + return c.NotFound(w, r, nil) + } else if err != nil { + return c.InternalError(err, w, r) + } + + if post.ID == posts[0].ID { + err = c.Convos.Delete(convo.ID) + } else { + err = post.Delete() + } + if err != nil { + return c.InternalError(err, w, r) + } + + http.Redirect(w, r, "/user/convo/"+strconv.Itoa(post.CID), http.StatusSeeOther) + return nil +} + +func ConvosEditReplySubmit(w http.ResponseWriter, r *http.Request, user c.User, scpid string) c.RouteError { _, ferr := c.SimpleUserCheck(w, r, &user) if ferr != nil { return ferr } http.Redirect(w, r, "/user/convo/id", http.StatusSeeOther) return nil -} \ No newline at end of file +} diff --git a/routes/reports.go b/routes/reports.go index f2a87793..167d4ee2 100644 --- a/routes/reports.go +++ b/routes/reports.go @@ -24,7 +24,8 @@ func ReportSubmit(w http.ResponseWriter, r *http.Request, user c.User, sitemID s // TODO: Localise these titles and bodies var title, content string - if itemType == "reply" { + switch itemType { + case "reply": reply, err := c.Rstore.Get(itemID) if err == sql.ErrNoRows { return c.LocalError("We were unable to find the reported post", w, r, user) @@ -41,7 +42,7 @@ func ReportSubmit(w http.ResponseWriter, r *http.Request, user c.User, sitemID s title = "Reply: " + topic.Title content = reply.Content + "\n\nOriginal Post: #rid-" + strconv.Itoa(itemID) - } else if itemType == "user-reply" { + case "user-reply": userReply, err := c.Prstore.Get(itemID) if err == sql.ErrNoRows { return c.LocalError("We weren't able to find the reported post", w, r, user) @@ -57,7 +58,7 @@ func ReportSubmit(w http.ResponseWriter, r *http.Request, user c.User, sitemID s } title = "Profile: " + profileOwner.Name content = userReply.Content + "\n\nOriginal Post: @" + strconv.Itoa(userReply.ParentID) - } else if itemType == "topic" { + case "topic": topic, err := c.Topics.Get(itemID) if err == sql.ErrNoRows { return c.NotFound(w, r, nil) @@ -66,7 +67,27 @@ func ReportSubmit(w http.ResponseWriter, r *http.Request, user c.User, sitemID s } title = "Topic: " + topic.Title content = topic.Content + "\n\nOriginal Post: #tid-" + strconv.Itoa(itemID) - } else { + case "convo-reply": + post := &c.ConversationPost{ID: itemID} + err := post.Fetch() + if err == sql.ErrNoRows { + return c.NotFound(w, r, nil) + } else if err != nil { + return c.InternalError(err, w, r) + } + + post, err = c.ConvoPostProcess.OnLoad(post) + if err != nil { + return c.InternalError(err, w, r) + } + user, err := c.Users.Get(post.CreatedBy) + if err != nil { + return c.InternalError(err, w, r) + } + + title = "Convo Post: " + user.Name + content = post.Body + "\n\nOriginal Post: #cpid-" + strconv.Itoa(itemID) + default: _, hasHook := headerLite.Hooks.VhookNeedHook("report_preassign", &itemID, &itemType) if hasHook { return nil diff --git a/schema/mssql/query_conversations.sql b/schema/mssql/query_conversations.sql new file mode 100644 index 00000000..591de621 --- /dev/null +++ b/schema/mssql/query_conversations.sql @@ -0,0 +1,8 @@ +CREATE TABLE [conversations] ( + [cid] int not null IDENTITY, + [createdBy] int not null, + [createdAt] datetime not null, + [lastReplyAt] datetime not null, + [lastReplyBy] int not null, + primary key([cid]) +); \ No newline at end of file diff --git a/schema/mssql/query_conversations_participants.sql b/schema/mssql/query_conversations_participants.sql new file mode 100644 index 00000000..027e302e --- /dev/null +++ b/schema/mssql/query_conversations_participants.sql @@ -0,0 +1,4 @@ +CREATE TABLE [conversations_participants] ( + [uid] int not null, + [cid] int not null +); \ No newline at end of file diff --git a/schema/mssql/query_conversations_posts.sql b/schema/mssql/query_conversations_posts.sql new file mode 100644 index 00000000..dc691be1 --- /dev/null +++ b/schema/mssql/query_conversations_posts.sql @@ -0,0 +1,8 @@ +CREATE TABLE [conversations_posts] ( + [pid] int not null IDENTITY, + [cid] int not null, + [createdBy] int not null, + [body] nvarchar (50) not null, + [post] nvarchar (50) DEFAULT '' not null, + primary key([pid]) +); \ No newline at end of file diff --git a/schema/mysql/query_conversations.sql b/schema/mysql/query_conversations.sql new file mode 100644 index 00000000..6dbdeb68 --- /dev/null +++ b/schema/mysql/query_conversations.sql @@ -0,0 +1,8 @@ +CREATE TABLE `conversations` ( + `cid` int not null AUTO_INCREMENT, + `createdBy` int not null, + `createdAt` datetime not null, + `lastReplyAt` datetime not null, + `lastReplyBy` int not null, + primary key(`cid`) +); \ No newline at end of file diff --git a/schema/mysql/query_conversations_participants.sql b/schema/mysql/query_conversations_participants.sql new file mode 100644 index 00000000..405273a9 --- /dev/null +++ b/schema/mysql/query_conversations_participants.sql @@ -0,0 +1,4 @@ +CREATE TABLE `conversations_participants` ( + `uid` int not null, + `cid` int not null +); \ No newline at end of file diff --git a/schema/mysql/query_conversations_posts.sql b/schema/mysql/query_conversations_posts.sql new file mode 100644 index 00000000..6921dd5f --- /dev/null +++ b/schema/mysql/query_conversations_posts.sql @@ -0,0 +1,8 @@ +CREATE TABLE `conversations_posts` ( + `pid` int not null AUTO_INCREMENT, + `cid` int not null, + `createdBy` int not null, + `body` varchar(50) not null, + `post` varchar(50) DEFAULT '' not null, + primary key(`pid`) +); \ No newline at end of file diff --git a/schema/pgsql/query_conversations.sql b/schema/pgsql/query_conversations.sql new file mode 100644 index 00000000..1c5f9345 --- /dev/null +++ b/schema/pgsql/query_conversations.sql @@ -0,0 +1,8 @@ +CREATE TABLE "conversations" ( + `cid` serial not null, + `createdBy` int not null, + `createdAt` timestamp not null, + `lastReplyAt` timestamp not null, + `lastReplyBy` int not null, + primary key(`cid`) +); \ No newline at end of file diff --git a/schema/pgsql/query_conversations_participants.sql b/schema/pgsql/query_conversations_participants.sql new file mode 100644 index 00000000..c7b5f860 --- /dev/null +++ b/schema/pgsql/query_conversations_participants.sql @@ -0,0 +1,4 @@ +CREATE TABLE "conversations_participants" ( + `uid` int not null, + `cid` int not null +); \ No newline at end of file diff --git a/schema/pgsql/query_conversations_posts.sql b/schema/pgsql/query_conversations_posts.sql new file mode 100644 index 00000000..3d206e3c --- /dev/null +++ b/schema/pgsql/query_conversations_posts.sql @@ -0,0 +1,8 @@ +CREATE TABLE "conversations_posts" ( + `pid` serial not null, + `cid` int not null, + `createdBy` int not null, + `body` varchar (50) not null, + `post` varchar (50) DEFAULT '' not null, + primary key(`pid`) +); \ No newline at end of file diff --git a/templates/account_menu.html b/templates/account_menu.html index d4dbb4d6..7355ed3f 100644 --- a/templates/account_menu.html +++ b/templates/account_menu.html @@ -9,6 +9,8 @@ + + {{/** TODO: Add an alerts page with pagination to go through alerts which either don't fit in the alerts drop-down or which have already been dismissed. Bear in mind though that dismissed alerts older than two weeks might be purged to save space and to speed up the database **/}}
diff --git a/templates/convo.html b/templates/convo.html index a6b39ef8..c3617af9 100644 --- a/templates/convo.html +++ b/templates/convo.html @@ -3,8 +3,4 @@

{{lang "convo_head"}}

-{{range .Posts}} -
-
{{.Body}}
-
-{{end}} \ No newline at end of file +
{{template "convo_row.html" . }}
\ No newline at end of file diff --git a/templates/convo_row.html b/templates/convo_row.html new file mode 100644 index 00000000..f6ca01de --- /dev/null +++ b/templates/convo_row.html @@ -0,0 +1,21 @@ +{{/** TODO: Temporary hack until we find a more granular way of doing this. Perhaps, a custom include function? **/}} +{{if .Header.Theme.BgAvatars}} +{{range .Posts}} +
+ {{.Body}} + + {{.User.Name}}   + + {{if $.CanModify}} + + {{end}} + + + + {{if .User.Tag}}{{.User.Tag}}{{end}} + +
+{{end}} +{{else}} +{{template "convo_row_alt.html" . }} +{{end}} \ No newline at end of file diff --git a/templates/convo_row_alt.html b/templates/convo_row_alt.html new file mode 100644 index 00000000..eb5ab6a1 --- /dev/null +++ b/templates/convo_row_alt.html @@ -0,0 +1,26 @@ +{{range .Posts}} +
+
+
+ + + {{.User.Name}} + {{if .User.Tag}}{{.User.Tag}}{{end}} + +
+ + {{if $.CanModify}} + + + + {{end}} + + + +
+
+ {{.Body}} +
+
+
+{{end}} \ No newline at end of file diff --git a/templates/convos.html b/templates/convos.html index ead11d29..16c04db8 100644 --- a/templates/convos.html +++ b/templates/convos.html @@ -1,7 +1,7 @@

{{lang "convos_head"}}

-

Create Convo

+

Create Convo

diff --git a/templates/header.html b/templates/header.html index a2489e3f..fd60ab2d 100644 --- a/templates/header.html +++ b/templates/header.html @@ -3,16 +3,16 @@ {{.Title}} | {{.Header.Site.Name}} {{range .Header.Stylesheets}} - {{end}} + {{end}} {{range .Header.PreScriptsAsync}} - {{end}} + {{end}} - + {{range .Header.ScriptsAsync}} - {{end}} - + {{end}} + {{range .Header.Scripts}} - {{end}} + {{end}} {{if .Header.MetaDesc}}{{end}} {{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}} diff --git a/templates/panel_themes.html b/templates/panel_themes.html index 590f6469..e941c7f4 100644 --- a/templates/panel_themes.html +++ b/templates/panel_themes.html @@ -3,10 +3,10 @@
{{range .PrimaryThemes}} -
- - {{.FriendlyName}}
- {{lang "panel_themes_author_prefix"}}{{.Creator}} +
+ + {{.FriendlyName}}
+ {{lang "panel_themes_author_prefix"}}{{.Creator}}
{{if .MobileFriendly}}📱{{end}} @@ -22,10 +22,10 @@
{{range .VariantThemes}} -
+
- {{.FriendlyName}}
- {{lang "panel_themes_author_prefix"}}{{.Creator}} + {{.FriendlyName}}
+ {{lang "panel_themes_author_prefix"}}{{.Creator}}
{{if .MobileFriendly}}📱{{end}} diff --git a/templates/profile.html b/templates/profile.html index 4c5de52e..46b71e45 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -27,9 +27,9 @@ {{if not .CurrentUser.Loggedin}}{{else}} - +
diff --git a/templates/profile_comments_row.html b/templates/profile_comments_row.html index 795d22a9..385ba5e9 100644 --- a/templates/profile_comments_row.html +++ b/templates/profile_comments_row.html @@ -1,21 +1,21 @@ {{/** TODO: Temporary hack until we find a more granular way of doing this. Perhaps, a custom include function? **/}} {{if .Header.Theme.BgAvatars}} - {{range .ItemList}} -
- {{.ContentHtml}} - - {{.CreatedByName}}   +{{range .ItemList}} +
+ {{.ContentHtml}} + + {{.CreatedByName}}   - {{if $.CurrentUser.IsMod}} + {{if $.CurrentUser.IsMod}} - {{end}} + {{end}} - + - {{if .Tag}}{{.Tag}}{{end}} - -
- {{end}} + {{if .Tag}}{{.Tag}}{{end}} +
+
+{{end}} {{else}} {{template "profile_comments_row_alt.html" . }} {{end}} \ No newline at end of file diff --git a/templates/profile_comments_row_alt.html b/templates/profile_comments_row_alt.html index 6d654880..73cd214a 100644 --- a/templates/profile_comments_row_alt.html +++ b/templates/profile_comments_row_alt.html @@ -1,24 +1,24 @@ {{range .ItemList}} -
-
-
- - - {{.CreatedByName}} - {{if .Tag}}{{.Tag}}{{end}} - -
- - {{if $.CurrentUser.IsMod}} - - - {{end}} - +
+
+
+ + + {{.CreatedByName}} + {{if .Tag}}{{.Tag}}{{end}}
-
- {{.ContentHtml}} -
+ + {{if $.CurrentUser.IsMod}} + + + {{end}} + +
-
+
+ {{.ContentHtml}} +
+
+
{{end}} \ No newline at end of file diff --git a/templates/topic.html b/templates/topic.html index a6132737..2c144266 100644 --- a/templates/topic.html +++ b/templates/topic.html @@ -31,7 +31,7 @@ {{end}}
-
+
{{.Topic.ContentHTML}}
{{if .CurrentUser.Loggedin}}{{end}} diff --git a/templates/topic_alt_quick_reply.html b/templates/topic_alt_quick_reply.html index 1c8d6de0..0f20e989 100644 --- a/templates/topic_alt_quick_reply.html +++ b/templates/topic_alt_quick_reply.html @@ -1,6 +1,6 @@
-
 
+
 
{{if .CurrentUser.Tag}}
{{else}}
{{end}} diff --git a/templates/topic_alt_userinfo.html b/templates/topic_alt_userinfo.html index f1e12107..6fcf21d7 100644 --- a/templates/topic_alt_userinfo.html +++ b/templates/topic_alt_userinfo.html @@ -1,5 +1,5 @@
-
 
+
 
{{if .Tag}}
{{else}}
{{end}} diff --git a/templates/topic_poll.html b/templates/topic_poll.html index 2581801f..6e8e9f69 100644 --- a/templates/topic_poll.html +++ b/templates/topic_poll.html @@ -1,5 +1,5 @@
-
+
{{range .Poll.QuickOptions}}
diff --git a/templates/topic_posts.html b/templates/topic_posts.html index 263a124b..376bf43f 100644 --- a/templates/topic_posts.html +++ b/templates/topic_posts.html @@ -5,7 +5,7 @@ {{.ActionType}}
{{else}} -
+
{{/** TODO: We might end up with
s in the inline editor, fix this **/}}
{{.ContentHtml}}
{{if $.CurrentUser.Loggedin}}
{{.Content}}
{{end}} diff --git a/themes/cosora/public/convo.css b/themes/cosora/public/convo.css new file mode 100644 index 00000000..e69de29b diff --git a/themes/cosora/public/misc.js b/themes/cosora/public/misc.js index 1f3b9510..7e53339a 100644 --- a/themes/cosora/public/misc.js +++ b/themes/cosora/public/misc.js @@ -17,7 +17,7 @@ //console.log("af") let loggedIn = document.head.querySelector("[property='x-loggedin']").content; if(loggedIn) { - if(navigator.userAgent.indexOf("Firefox") != -1) $.trumbowyg.svgPath = "/static/trumbowyg/ui/icons.svg"; + if(navigator.userAgent.indexOf("Firefox") != -1) $.trumbowyg.svgPath = "/s/trumbowyg/ui/icons.svg"; // Is there we way we can append instead? Maybe, an editor plugin? attachItemCallback = function(attachItem) { diff --git a/themes/nox/public/convo.css b/themes/nox/public/convo.css new file mode 100644 index 00000000..8fa5be33 --- /dev/null +++ b/themes/nox/public/convo.css @@ -0,0 +1,30 @@ +.rowitem .topRow { + display: flex; + width: 100%; +} +.rowitem .userbit { + display: flex; +} +.rowitem .topRow .nameAndTitle { + display: flex; + flex-direction: column; + margin-left: 8px; +} +.nameAndTitle .real_username { + font-size: 17px; + line-height: 16px; +} +.userbit img { + width: 40px; + height: 40px; + border-radius: 24px; +} +.controls { + margin-left: auto; +} +.controls a { + margin-right: 8px; +} +.content_column { + margin-top: 5px; +} \ No newline at end of file diff --git a/themes/nox/public/main.css b/themes/nox/public/main.css index a815c960..a092ed0c 100644 --- a/themes/nox/public/main.css +++ b/themes/nox/public/main.css @@ -351,6 +351,10 @@ h1, h2, h3, h4, h5 { .rowhead h1, .opthead h1, .colstack_head h1 { font-size: 21px; } +.rowhead h1 + h2, .opthead h1 + h2, .colstack_right .colstack_head .rowitem h1 + h2 { + margin-left: auto; +} + .sidebar .rowhead { margin-left: 18px; margin-top: 4px; diff --git a/themes/nox/public/profile.css b/themes/nox/public/profile.css index 3b612579..09228717 100644 --- a/themes/nox/public/profile.css +++ b/themes/nox/public/profile.css @@ -16,8 +16,6 @@ width: 64px; height: 64px; border-radius: 32px; - margin-left: auto; - margin-right: auto; } .nameRow { display: flex; diff --git a/themes/shadow/public/convo.css b/themes/shadow/public/convo.css new file mode 100644 index 00000000..e69de29b diff --git a/themes/tempra_simple/public/convo.css b/themes/tempra_simple/public/convo.css new file mode 100644 index 00000000..e69de29b