More conversations work.

Add support for AS in columns for SimpleInnerJoin.
Add a referrer policy to improve privacy a little.
Shorten /static/ to /s/ since it comes up so much.
Remove some obsolete code.
Shorten some variable names.
Reduce the amount of boilerplate in the patcher.

Added the RefNoTrack and RefNoRef privacy config settings.

You may need to run the updater / patcher for this commit.
This commit is contained in:
Azareal 2019-08-14 20:39:04 +10:00
parent 6ba4b665e3
commit 4d8c97812d
54 changed files with 901 additions and 456 deletions

View File

@ -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{

View File

@ -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
}
}

View File

@ -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
}
}
func (co *ConversationPost) Delete() error {
_, err := convoStmts.deletePost.Exec(co.ID)
return err
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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.

View File

@ -1,9 +1,9 @@
<div class="rowitem passive deletable_block editable_parent post_item" style="background-color: #eaeaea;padding-top: 3px;padding-left: 4px;clear: both;border-bottom: solid 1px #ccc;padding-right: 3px;padding-bottom: 6px;">
<div class="userinfo" style="background: white;width: 132px;padding: 2px;margin-top: 2px;float: left;">
<div class="avatar_item" style="background-image: url(/uploads/avatar_1.jpg), url(/static/white-dot.jpg);background-position: 0px -10px;background-repeat: no-repeat, repeat-y;background-size: 128px;width: 128px;height: 100%;min-height: 128px;border-style: solid;border-color: #eaeaea;border-width: 1px;">&nbsp;</div>
<div class="avatar_item" style="background-image: url(/uploads/avatar_1.jpg), url(/s/white-dot.jpg);background-position:0px -10px;background-repeat:no-repeat, repeat-y;background-size:128px;width:128px;height:100%;min-height: 128px;border-style:solid;border-color:#eaeaea;border-width:1px;">&nbsp;</div>
<div class="the_name" style="margin-top: 3px;text-align: center;color: #505050;">Azareal</div>
</div>
<div class="content_container" style="background: white;margin-left: 137px;min-height: 128px;margin-bottom: 0;margin-right: 3px;">
<div class="content_container" style="background:white;margin-left:137px;min-height:128px;margin-bottom:0;margin-right:3px;">
<div class="editable_block user_content" style="padding: 5px;margin-top: 3px;margin-bottom: 0;background: white;min-height: 133px;padding-bottom: 0;width: 100%;">boo</div>
<div class="button_container" style="border-top: solid 1px #eaeaea;border-spacing: 0px;border-collapse: collapse;padding: 0;margin: 0;display: block;">
<a style="border-right: solid 1px #eaeaea;color: #505050;font-size: 13px;padding-left: 5px;padding-right: 5px;">Edit</a>

View File

@ -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

View File

@ -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)

View File

@ -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.",

View File

@ -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 {

View File

@ -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,
))
}

View File

@ -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);
}
*/

View File

@ -1,4 +1,4 @@
$.emojiarea.path = '/static/smilies/emojiarea';
$.emojiarea.path = '/s/smilies/emojiarea';
$.emojiarea.icons = [{"name" : "<i class='icon-smile'></i>", "icons" : {':bowtie:' : 'bowtie.png',
':smile:' : 'smile.png',
':laughing:' : 'laughing.png',

View File

@ -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 {

View File

@ -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":

View File

@ -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"),

View File

@ -21,8 +21,8 @@ func ParseSEOURL(urlBit string) (slug string, id int, err error) {
return halves[0], tid, err
}
var slen1 = len("</static/>; rel=preload; as=script,")
var slen2 = len("</static/>; rel=preload; as=style,")
var slen1 = len("</s/>; rel=preload; as=script,")
var slen2 = len("</s/>; 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("</static/")
sb.WriteString("</s/")
sb.WriteString(path)
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("</static/")
sb.WriteString("</s/")
sb.WriteString(path)
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
var RenderTemplate = renderTemplate3

View File

@ -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
}
}

View File

@ -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

View File

@ -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])
);

View File

@ -0,0 +1,4 @@
CREATE TABLE [conversations_participants] (
[uid] int not null,
[cid] int not null
);

View File

@ -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])
);

View File

@ -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`)
);

View File

@ -0,0 +1,4 @@
CREATE TABLE `conversations_participants` (
`uid` int not null,
`cid` int not null
);

View File

@ -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`)
);

View File

@ -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`)
);

View File

@ -0,0 +1,4 @@
CREATE TABLE "conversations_participants" (
`uid` int not null,
`cid` int not null
);

View File

@ -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`)
);

View File

@ -9,6 +9,8 @@
<div class="rowitem passive"><a href="/user/edit/email/">{{lang "account_menu_email"}}</a></div>
<!--<div class="rowitem passive"><a href="/user/edit/notifications/">{{lang "account_menu_notifications"}}</a> <span class="account_soon">Coming Soon</span></div>-->
<div class="rowitem passive"><a href="/user/edit/logins/">{{lang "account_menu_logins"}}</a></div>
<!--<div class="rowitem passive"><a href="/user/edit/penalties/">{{lang "account_menu_penalties"}}</a></div>-->
<div class="rowitem passive"><a href="/user/convos/">{{lang "account_menu_messages"}}</a></div>
{{/** 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 **/}}
</div>
</nav>

View File

@ -3,8 +3,4 @@
<h1>{{lang "convo_head"}}</h1>
</div>
</div>
{{range .Posts}}
<div class="colstack_item">
<div class="rowitem">{{.Body}}</div>
</div>
{{end}}
<div class="colstack_item hash_hide">{{template "convo_row.html" . }}</div>

21
templates/convo_row.html Normal file
View File

@ -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}}
<div id="post-{{.ID}}" class="rowitem passive deletable_block editable_parent simple {{.ClassName}}" style="background-image:url({{.User.Avatar}}),url(/s/post-avatar-bg.jpg);background-position:0px {{if le .ContentLines 5}}-1{{end}}0px;">
<span class="editable_block user_content simple">{{.Body}}</span>
<span class="controls">
<a href="{{.User.Link}}" class="real_username username">{{.User.Name}}</a>&nbsp;&nbsp;
{{if $.CanModify}}<a href="/user/convo/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
<a href="/user/convo/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=convo-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
{{if .User.Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.User.Tag}}</a>{{end}}
</span>
</div>
{{end}}
{{else}}
{{template "convo_row_alt.html" . }}
{{end}}

View File

@ -0,0 +1,26 @@
{{range .Posts}}
<div id="post-{{.ID}}" class="rowitem passive deletable_block editable_parent comment {{.ClassName}}">
<div class="topRow">
<div class="userbit">
<img src="{{.User.MicroAvatar}}" alt="Avatar" title="{{.User.Name}}'s Avatar" aria-hidden="true" />
<span class="nameAndTitle">
<a href="{{.User.Link}}" class="real_username username">{{.User.Name}}</a>
{{if .User.Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.User.Tag}}</a>{{end}}
</span>
</div>
<span class="controls">
{{if $.CanModify}}
<a href="/user/convo/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
<a href="/user/convo/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
{{end}}
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=convo-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
</span>
</div>
<div class="content_column">
<span class="editable_block user_content">{{.Body}}</span>
</div>
</div>
<div class="after_comment"></div>
{{end}}

View File

@ -1,7 +1,7 @@
<div class="colstack_item colstack_head rowhead">
<div class="rowitem">
<h1>{{lang "convos_head"}}</h1>
<h2>Create Convo</h2>
<h2><a href="/user/convos/create/">Create Convo</a></h2>
</div>
</div>
<div class="colstack_item">

View File

@ -3,16 +3,16 @@
<head>
<title>{{.Title}} | {{.Header.Site.Name}}</title>
{{range .Header.Stylesheets}}
<link href="/static/{{.}}" rel="stylesheet" type="text/css">{{end}}
<link href="/s/{{.}}" rel="stylesheet" type="text/css">{{end}}
{{range .Header.PreScriptsAsync}}
<script async type="text/javascript" src="/static/{{.}}"></script>{{end}}
<script async type="text/javascript" src="/s/{{.}}"></script>{{end}}
<meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" />
<script type="text/javascript" src="/static/init.js?i=8"></script>
<script type="text/javascript" src="/s/init.js?i=9"></script>
{{range .Header.ScriptsAsync}}
<script async type="text/javascript" src="/static/{{.}}"></script>{{end}}
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
<script async type="text/javascript" src="/s/{{.}}"></script>{{end}}
<script type="text/javascript" src="/s/jquery-3.1.1.min.js"></script>
{{range .Header.Scripts}}
<script type="text/javascript" src="/static/{{.}}"></script>{{end}}
<script type="text/javascript" src="/s/{{.}}"></script>{{end}}
<meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" />
{{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}}
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}

View File

@ -3,10 +3,10 @@
</div>
<div id="panel_primary_themes" class="colstack_item panel_themes complex_rowlist">
{{range .PrimaryThemes}}
<div class="theme_row rowitem editable_parent"{{if .FullImage}} style="background-image: url('/static/{{.FullImage}}');background-position: center;background-size: 50%;background-repeat: no-repeat;"{{end}}>
<span style="float: left;">
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size: 17px;">{{.FriendlyName}}</a><br />
<small class="panel_theme_author" style="margin-left: 2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
<div class="theme_row rowitem editable_parent"{{if .FullImage}} style="background-image:url('/s/{{.FullImage}}');background-position:center;background-size:50%;background-repeat:no-repeat;"{{end}}>
<span style="float:left;">
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size:17px;">{{.FriendlyName}}</a><br />
<small class="panel_theme_author" style="margin-left:2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
</span>
<span class="panel_floater">
{{if .MobileFriendly}}<span class="panel_tag panel_theme_mobile" title="{{lang "panel_themes_mobile_friendly_tooltip"}}" aria-label="{{lang "panel_themes_mobile_friendly_aria"}}">📱</span>{{end}}
@ -22,10 +22,10 @@
</div>
<div id="panel_variant_themes" class="colstack_item panel_themes">
{{range .VariantThemes}}
<div class="theme_row rowitem editable_parent"{{if .FullImage}} style="background-image: url('/static/{{.FullImage}}');background-position: center;background-size: 50%;background-repeat: no-repeat;"{{end}}>
<div class="theme_row rowitem editable_parent"{{if .FullImage}} style="background-image:url('/s/{{.FullImage}}');background-position:center;background-size:50%;background-repeat:no-repeat;"{{end}}>
<span style="float: left;">
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size: 17px;">{{.FriendlyName}}</a><br />
<small class="panel_theme_author" style="margin-left: 2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
<a href="/panel/themes/{{.Name}}" class="editable_block" style="font-size:17px;">{{.FriendlyName}}</a><br />
<small class="panel_theme_author" style="margin-left:2px;">{{lang "panel_themes_author_prefix"}}<a href="//{{.URL}}">{{.Creator}}</a></small>
</span>
<span class="panel_floater">
{{if .MobileFriendly}}<span class="panel_tag panel_theme_mobile" title="{{lang "panel_themes_mobile_friendly_tooltip"}}" aria-label="{{lang "panel_themes_mobile_friendly_aria"}}">📱</span>{{end}}

View File

@ -27,9 +27,9 @@
{{if not .CurrentUser.Loggedin}}<div class="rowitem passive">
<a class="profile_menu_item">{{lang "profile_login_for_options"}}</a>
</div>{{else}}
<!--<div class="rowitem passive">
<div class="rowitem passive">
<a href="/user/convos/create/" class="profile_menu_item">{{lang "profile_send_message"}}</a>
</div>-->
</div>
<!--<div class="rowitem passive">
<a class="profile_menu_item">{{lang "profile_add_friend"}}</a>
</div>-->

View File

@ -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}}
<div id="post-{{.ID}}" class="rowitem passive deletable_block editable_parent simple {{.ClassName}}" style="background-image:url({{.Avatar}}),url(/static/post-avatar-bg.jpg);background-position:0px {{if le .ContentLines 5}}-1{{end}}0px;">
<span class="editable_block user_content simple">{{.ContentHtml}}</span>
<span class="controls">
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>&nbsp;&nbsp;
{{range .ItemList}}
<div id="post-{{.ID}}" class="rowitem passive deletable_block editable_parent simple {{.ClassName}}" style="background-image:url({{.Avatar}}),url(/s/post-avatar-bg.jpg);background-position:0px {{if le .ContentLines 5}}-1{{end}}0px;">
<span class="editable_block user_content simple">{{.ContentHtml}}</span>
<span class="controls">
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>&nbsp;&nbsp;
{{if $.CurrentUser.IsMod}}<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
{{if $.CurrentUser.IsMod}}<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.Tag}}</a>{{end}}
</span>
</div>
{{end}}
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.Tag}}</a>{{end}}
</span>
</div>
{{end}}
{{else}}
{{template "profile_comments_row_alt.html" . }}
{{end}}

View File

@ -1,24 +1,24 @@
{{range .ItemList}}
<div id="post-{{.ID}}" class="rowitem passive deletable_block editable_parent comment {{.ClassName}}">
<div class="topRow">
<div class="userbit">
<img src="{{.MicroAvatar}}" alt="Avatar" title="{{.CreatedByName}}'s Avatar" aria-hidden="true" />
<span class="nameAndTitle">
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.Tag}}</a>{{end}}
</span>
</div>
<span class="controls">
{{if $.CurrentUser.IsMod}}
<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
{{end}}
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
<div id="post-{{.ID}}" class="rowitem passive deletable_block editable_parent comment {{.ClassName}}">
<div class="topRow">
<div class="userbit">
<img src="{{.MicroAvatar}}" alt="Avatar" title="{{.CreatedByName}}'s Avatar" aria-hidden="true" />
<span class="nameAndTitle">
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.Tag}}</a>{{end}}
</span>
</div>
<div class="content_column">
<span class="editable_block user_content">{{.ContentHtml}}</span>
</div>
<span class="controls">
{{if $.CurrentUser.IsMod}}
<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
{{end}}
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
</span>
</div>
<div class="after_comment"></div>
<div class="content_column">
<span class="editable_block user_content">{{.ContentHtml}}</span>
</div>
</div>
<div class="after_comment"></div>
{{end}}

View File

@ -31,7 +31,7 @@
{{end}}
<article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic.opening_post_aria"}}">
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image:url({{.Topic.Avatar}}), url(/s/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
<div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div>
{{if .CurrentUser.Loggedin}}<textarea name="topic_content" class="show_on_edit topic_content_input edit_source">{{.Topic.Content}}</textarea>{{end}}

View File

@ -1,6 +1,6 @@
<div class="rowblock topic_reply_container">
<div class="userinfo" aria-label="{{lang "topic.your_information"}}">
<div class="avatar_item" style="background-image:url({{.CurrentUser.Avatar}}),url(/static/white-dot.jpg);background-position:0px -10px;">&nbsp;</div>
<div class="avatar_item" style="background-image:url({{.CurrentUser.Avatar}}),url(/s/white-dot.jpg);background-position:0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a>
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}

View File

@ -1,5 +1,5 @@
<div class="userinfo" aria-label="{{lang "topic.userinfo_aria"}}">
<div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<div class="avatar_item" style="background-image:url({{.Avatar}}), url(/s/white-dot.jpg);background-position:0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a>
{{if .Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Level}}</div><div class="tag_post"></div></div>{{end}}

View File

@ -1,5 +1,5 @@
<article class="rowblock post_container poll" aria-level="{{lang "topic.poll_aria"}}">
<div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
<div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image:url({{.Topic.Avatar}}), url(/s/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
<div class="topic_content user_content">
{{range .Poll.QuickOptions}}
<div class="poll_option">

View File

@ -5,7 +5,7 @@
<span itemprop="text">{{.ActionType}}</span>
</article>
{{else}}
<article {{scope "post"}} id="post-{{.ID}}" itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{.ClassName}}" style="background-image: url({{.Avatar}}), url(/static/{{$.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
<article {{scope "post"}} id="post-{{.ID}}" itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{.ClassName}}" style="background-image:url({{.Avatar}}), url(/s/{{$.Header.Theme.Name}}/post-avatar-bg.jpg);background-position:0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
{{/** TODO: We might end up with <br>s in the inline editor, fix this **/}}
<div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div>
{{if $.CurrentUser.Loggedin}}<div class="auto_hide edit_source">{{.Content}}</div>{{end}}

View File

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -16,8 +16,6 @@
width: 64px;
height: 64px;
border-radius: 32px;
margin-left: auto;
margin-right: auto;
}
.nameRow {
display: flex;

View File

View File