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