NoEmbeds privacy and config settings.

Phase out url_prefix and url_name.
Eliminate some boilerplate and allocations while we're at it.
Reduce the number of conditional statements in url validator loops.
Better parsing of Site.URL to handle user error better.

You will have to run the patcher / updater for this commit.
This commit is contained in:
Azareal 2019-12-08 13:40:56 +10:00
parent 3715c800da
commit 0dede6a329
31 changed files with 689 additions and 561 deletions

View File

@ -31,11 +31,15 @@ func createTables(adapter qgen.Adapter) (err error) {
tC{"session", "varchar", 200, false, false, "''"}, tC{"session", "varchar", 200, false, false, "''"},
//tC{"authToken", "varchar", 200, false, false, "''"}, //tC{"authToken", "varchar", 200, false, false, "''"},
tC{"last_ip", "varchar", 200, false, false, "0.0.0.0.0"}, tC{"last_ip", "varchar", 200, false, false, "0.0.0.0.0"},
tC{"enable_embeds", "int", 0, false, false, "-1"},
tC{"email", "varchar", 200, false, false, "''"}, tC{"email", "varchar", 200, false, false, "''"},
tC{"avatar", "varchar", 100, false, false, "''"}, tC{"avatar", "varchar", 100, false, false, "''"},
tC{"message", "text", 0, false, false, "''"}, tC{"message", "text", 0, false, false, "''"},
// TODO: Drop these columns?
tC{"url_prefix", "varchar", 20, false, false, "''"}, tC{"url_prefix", "varchar", 20, false, false, "''"},
tC{"url_name", "varchar", 100, false, false, "''"}, tC{"url_name", "varchar", 100, false, false, "''"},
tC{"level", "smallint", 0, false, false, "0"}, tC{"level", "smallint", 0, false, false, "0"},
tC{"score", "int", 0, false, false, "0"}, tC{"score", "int", 0, false, false, "0"},
tC{"posts", "int", 0, false, false, "0"}, tC{"posts", "int", 0, false, false, "0"},

View File

@ -248,6 +248,13 @@ type AccountBlocksPage struct {
Paginator Paginator
} }
type AccountPrivacyPage struct {
*Header
ProfileComments bool
ReceiveConvos bool
EnableEmbeds bool
}
type AccountDashPage struct { type AccountDashPage struct {
*Header *Header
MFASetup bool MFASetup bool

View File

@ -449,10 +449,26 @@ var hashLinkMap = map[string]func(*strings.Builder, string, *int){
// TODO: Forum Shortcode Link // TODO: Forum Shortcode Link
} }
// TODO: Pack multiple bit flags into an integer instead of using a struct?
var DefaultParseSettings = &ParseSettings{}
type ParseSettings struct {
NoEmbed bool
}
func (ps *ParseSettings) CopyPtr() *ParseSettings {
n := &ParseSettings{}
*n = *ps
return n
}
// TODO: Write a test for this // TODO: Write a test for this
// TODO: We need a lot more hooks here. E.g. To add custom media types and handlers. // TODO: We need a lot more hooks here. E.g. To add custom media types and handlers.
// TODO: Use templates to reduce the amount of boilerplate? // TODO: Use templates to reduce the amount of boilerplate?
func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/) string { func ParseMessage(msg string, sectionID int, sectionType string, settings *ParseSettings /*, user User*/) string {
if settings == nil {
settings = DefaultParseSettings
}
// TODO: Word boundary detection for these to avoid mangling code // TODO: Word boundary detection for these to avoid mangling code
msg = strings.Replace(msg, ":)", "😀", -1) msg = strings.Replace(msg, ":)", "😀", -1)
msg = strings.Replace(msg, ":(", "😞", -1) msg = strings.Replace(msg, ":(", "😞", -1)
@ -544,10 +560,10 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
i-- i--
} else if msg[i] == 'h' || msg[i] == 'f' || msg[i] == 'g' || msg[i] == '/' { } else if msg[i] == 'h' || msg[i] == 'f' || msg[i] == 'g' || msg[i] == '/' {
//fmt.Println("s3") //fmt.Println("s3")
if len(msg) > i+3 && msg[i+1] == 't' && msg[i+2] == 't' && msg[i+3] == 'p' { if len(msg) > i+5 && msg[i+1] == 't' && msg[i+2] == 't' && msg[i+3] == 'p' {
if len(msg) > i+6 && msg[i+4] == 's' && msg[i+5] == ':' && msg[i+6] == '/' { if len(msg) > i+6 && msg[i+4] == 's' && msg[i+5] == ':' && msg[i+6] == '/' {
// Do nothing // Do nothing
} else if len(msg) > i+5 && msg[i+4] == ':' && msg[i+5] == '/' { } else if msg[i+4] == ':' && msg[i+5] == '/' {
// Do nothing // Do nothing
} else { } else {
continue continue
@ -593,7 +609,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
continue continue
} }
media, ok := parseMediaString(msg[i : i+urlLen]) media, ok := parseMediaString(msg[i:i+urlLen], settings)
if !ok { if !ok {
//fmt.Println("o3") //fmt.Println("o3")
sb.Write(InvalidURL) sb.Write(InvalidURL)
@ -702,8 +718,8 @@ func validateURLString(data string) bool {
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s. // ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
for ; len(data) > i; i++ { for ; len(data) > i; i++ {
ch := data[i] // char ch := data[i]
if ch != '\\' && ch != '_' && ch != ':' && ch != '?' && ch != '&' && ch != '=' && ch != ';' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 58) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 90 is Z, 91 is [ if ch != '\\' && ch != '_' && ch != '?' && ch != '&' && ch != '=' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 60) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 57 is 9, 58 is :, 59 is ;, 90 is Z, 91 is [
return false return false
} }
} }
@ -728,8 +744,8 @@ func validatedURLBytes(data []byte) (url []byte) {
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s. // ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
for ; datalen > i; i++ { for ; datalen > i; i++ {
ch := data[i] // char ch := data[i]
if ch != '\\' && ch != '_' && ch != ':' && ch != '?' && ch != '&' && ch != '=' && ch != ';' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 58) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 90 is Z, 91 is [ if ch != '\\' && ch != '_' && ch != '?' && ch != '&' && ch != '=' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 60) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 57 is 9, 58 is :, 59 is ;, 90 is Z, 91 is [
return InvalidURL return InvalidURL
} }
} }
@ -756,8 +772,8 @@ func PartialURLString(data string) (url []byte) {
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s. // ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
for ; end >= i; i++ { for ; end >= i; i++ {
ch := data[i] // char ch := data[i]
if ch != '\\' && ch != '_' && ch != ':' && ch != '?' && ch != '&' && ch != '=' && ch != ';' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 58) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 90 is Z, 91 is [ if ch != '\\' && ch != '_' && ch != '?' && ch != '&' && ch != '=' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 60) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 57 is 9, 58 is :, 59 is ;, 90 is Z, 91 is [
end = i end = i
} }
} }
@ -796,7 +812,7 @@ func PartialURLStringLen(data string) (int, bool) {
if ch < 33 { // space and invisibles if ch < 33 { // space and invisibles
//fmt.Println("e2:",i) //fmt.Println("e2:",i)
return i, i != f return i, i != f
} else if ch != '\\' && ch != '_' && ch != ':' && ch != '?' && ch != '&' && ch != '=' && ch != ';' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 58) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 90 is Z, 91 is [ } else if ch != '\\' && ch != '_' && ch != '?' && ch != '&' && ch != '=' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 60) && !(ch > 64 && ch < 92) && !(ch > 96 && ch < 123) { // 57 is 9, 58 is :, 59 is ;, 90 is Z, 91 is [
//log.Print("Bad Character: ", ch) //log.Print("Bad Character: ", ch)
//fmt.Println("e3") //fmt.Println("e3")
return i, false return i, false
@ -830,8 +846,8 @@ func PartialURLStringLen2(data string) int {
// ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s. // ? - There should only be one : and that's only if the URL is on a non-standard port. Same for ?s.
for ; len(data) > i; i++ { for ; len(data) > i; i++ {
ch := data[i] //char ch := data[i]
if ch != '\\' && ch != '_' && ch != ':' && ch != '?' && ch != '&' && ch != '=' && ch != ';' && ch != '@' && ch != '#' && !(ch > 44 && ch < 58) && !(ch > 64 && ch < 91) && !(ch > 96 && ch < 123) { // 90 is Z, 91 is [ if ch != '\\' && ch != '_' && ch != '?' && ch != '&' && ch != '=' && ch != '@' && ch != '#' && ch != ']' && !(ch > 44 && ch < 60) && !(ch > 64 && ch < 91) && !(ch > 96 && ch < 123) { // 57 is 9, 58 is :, 59 is ;, 90 is Z, 91 is [
//log.Print("Bad Character: ", ch) //log.Print("Bad Character: ", ch)
return i return i
} }
@ -850,7 +866,7 @@ type MediaEmbed struct {
} }
// TODO: Write a test for this // TODO: Write a test for this
func parseMediaString(data string) (media MediaEmbed, ok bool) { func parseMediaString(data string, settings *ParseSettings) (media MediaEmbed, ok bool) {
if !validateURLString(data) { if !validateURLString(data) {
return media, false return media, false
} }
@ -907,6 +923,7 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) {
} }
} }
if !settings.NoEmbed {
// ? - I don't think this hostname will hit every YT domain // ? - I don't think this hostname will hit every YT domain
// TODO: Make this a more customisable handler rather than hard-coding it in here // TODO: Make this a more customisable handler rather than hard-coding it in here
if strings.HasSuffix(host, ".youtube.com") && path == "/watch" { if strings.HasSuffix(host, ".youtube.com") && path == "/watch" {
@ -934,6 +951,7 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) {
} }
} }
} }
}
var sport string var sport string
if port != "443" && port != "80" && port != "" { if port != "443" && port != "80" && port != "" {
@ -947,8 +965,8 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) {
if len(uurl.Fragment) > 0 { if len(uurl.Fragment) > 0 {
frag = "#" + uurl.Fragment frag = "#" + uurl.Fragment
} }
media.URL = scheme + "//" + host + sport + path + q + frag
media.FURL = host + sport + path + q + frag media.FURL = host + sport + path + q + frag
media.URL = scheme + "//" + media.FURL
return media, true return media, true
} }
@ -978,7 +996,7 @@ func CoerceIntString(data string) (res int, length int) {
// TODO: Write tests for this // TODO: Write tests for this
// Make sure we reflect changes to this in the JS port in /public/global.js // Make sure we reflect changes to this in the JS port in /public/global.js
func Paginate(currentPage int, lastPage int, maxPages int) (out []int) { func Paginate(currentPage, lastPage, maxPages int) (out []int) {
diff := lastPage - currentPage diff := lastPage - currentPage
pre := 3 pre := 3
if diff < 3 { if diff < 3 {
@ -998,7 +1016,7 @@ func Paginate(currentPage int, lastPage int, maxPages int) (out []int) {
// TODO: Write tests for this // TODO: Write tests for this
// Make sure we reflect changes to this in the JS port in /public/global.js // Make sure we reflect changes to this in the JS port in /public/global.js
func PageOffset(count int, page int, perPage int) (int, int, int) { func PageOffset(count, page, perPage int) (int, int, int) {
var offset int var offset int
lastPage := LastPage(count, perPage) lastPage := LastPage(count, perPage)
if page > 1 { if page > 1 {
@ -1020,6 +1038,6 @@ func PageOffset(count int, page int, perPage int) (int, int, int) {
// TODO: Write tests for this // TODO: Write tests for this
// Make sure we reflect changes to this in the JS port in /public/global.js // Make sure we reflect changes to this in the JS port in /public/global.js
func LastPage(count int, perPage int) int { func LastPage(count, perPage int) int {
return (count / perPage) + 1 return (count / perPage) + 1
} }

View File

@ -5,7 +5,7 @@ import (
"html" "html"
"time" "time"
"github.com/Azareal/Gosora/query_gen" qgen "github.com/Azareal/Gosora/query_gen"
) )
var profileReplyStmts ProfileReplyStmts var profileReplyStmts ProfileReplyStmts
@ -30,9 +30,10 @@ type ProfileReplyStmts struct {
func init() { func init() {
DbInits.Add(func(acc *qgen.Accumulator) error { DbInits.Add(func(acc *qgen.Accumulator) error {
ur := "users_replies"
profileReplyStmts = ProfileReplyStmts{ profileReplyStmts = ProfileReplyStmts{
edit: acc.Update("users_replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(), edit: acc.Update(ur).Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(),
delete: acc.Delete("users_replies").Where("rid = ?").Prepare(), delete: acc.Delete(ur).Where("rid = ?").Prepare(),
} }
return acc.FirstError() return acc.FirstError()
}) })
@ -51,7 +52,7 @@ func (r *ProfileReply) Delete() error {
func (r *ProfileReply) SetBody(content string) error { func (r *ProfileReply) SetBody(content string) error {
content = PreparseMessage(html.UnescapeString(content)) content = PreparseMessage(html.UnescapeString(content))
_, err := profileReplyStmts.edit.Exec(content, ParseMessage(content, 0, ""), r.ID) _, err := profileReplyStmts.edit.Exec(content, ParseMessage(content, 0, "", nil), r.ID)
return err return err
} }

View File

@ -38,7 +38,7 @@ func (s *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) {
} }
func (s *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error) { func (s *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error) {
res, err := s.create.Exec(profileID, content, ParseMessage(content, 0, ""), createdBy, ipaddress) res, err := s.create.Exec(profileID, content, ParseMessage(content, 0, "", nil), createdBy, ipaddress)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -17,31 +17,18 @@ import (
type ReplyUser struct { type ReplyUser struct {
Reply Reply
//ID int
//ParentID int
//Content string
ContentHtml string ContentHtml string
//CreatedBy int
UserLink string UserLink string
CreatedByName string CreatedByName string
//Group int
//CreatedAt time.Time
//LastEdit int
//LastEditBy int
Avatar string Avatar string
MicroAvatar string MicroAvatar string
ClassName string ClassName string
//ContentLines int
Tag string Tag string
URL string URL string
URLPrefix string //URLPrefix string
URLName string //URLName string
Level int Level int
//IP string
//Liked bool
//LikeCount int
//AttachCount int
//ActionType string
ActionIcon string ActionIcon string
Attachments []*MiniAttachment Attachments []*MiniAttachment
@ -139,7 +126,7 @@ func (r *Reply) SetPost(content string) error {
return err return err
} }
content = PreparseMessage(html.UnescapeString(content)) content = PreparseMessage(html.UnescapeString(content))
parsedContent := ParseMessage(content, topic.ParentID, "forums") parsedContent := ParseMessage(content, topic.ParentID, "forums", nil)
_, err = replyStmts.edit.Exec(content, parsedContent, r.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll _, err = replyStmts.edit.Exec(content, parsedContent, r.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll
_ = Rstore.GetCache().Remove(r.ID) _ = Rstore.GetCache().Remove(r.ID)
return err return err

View File

@ -52,7 +52,7 @@ func (s *SQLReplyStore) Get(id int) (*Reply, error) {
// TODO: Write a test for this // TODO: Write a test for this
func (s *SQLReplyStore) Create(t *Topic, content string, ip string, uid int) (rid int, err error) { func (s *SQLReplyStore) Create(t *Topic, content string, ip string, uid int) (rid int, err error) {
res, err := s.create.Exec(t.ID, content, ParseMessage(content, t.ParentID, "forums"), ip, WordCount(content), uid) res, err := s.create.Exec(t.ID, content, ParseMessage(content, t.ParentID, "forums", nil), ip, WordCount(content), uid)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -34,7 +34,7 @@ func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, error) {
} }
// ! There's a data race in this. If two users report one item at the exact same time, then both reports will go through // ! There's a data race in this. If two users report one item at the exact same time, then both reports will go through
func (s *DefaultReportStore) Create(title string, content string, user *User, itemType string, itemID int) (tid int, err error) { func (s *DefaultReportStore) Create(title string, content string, u *User, itemType string, itemID int) (tid int, err error) {
var count int var count int
err = s.exists.QueryRow(itemType+"_"+strconv.Itoa(itemID), ReportForumID).Scan(&count) err = s.exists.QueryRow(itemType+"_"+strconv.Itoa(itemID), ReportForumID).Scan(&count)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
@ -44,7 +44,7 @@ func (s *DefaultReportStore) Create(title string, content string, user *User, it
return 0, ErrAlreadyReported return 0, ErrAlreadyReported
} }
res, err := s.create.Exec(title, content, ParseMessage(content, 0, ""), user.LastIP, user.ID, user.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID) res, err := s.create.Exec(title, content, ParseMessage(content, 0, "", nil), u.LastIP, u.ID, u.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -53,5 +53,6 @@ func (s *DefaultReportStore) Create(title string, content string, user *User, it
return 0, err return 0, err
} }
tid = int(lastID) tid = int(lastID)
return tid, Forums.AddTopic(tid, user.ID, ReportForumID)
return tid, Forums.AddTopic(tid, u.ID, ReportForumID)
} }

View File

@ -91,6 +91,7 @@ type config struct {
PrimaryServer bool PrimaryServer bool
ServerCount int ServerCount int
LastIPCutoff int // Currently just -1, non--1, but will accept the number of months a user's last IP should be retained for in the future before being purged. Please note that the other two cutoffs below operate off the numbers of days instead.
PostIPCutoff int PostIPCutoff int
LogPruneCutoff int LogPruneCutoff int
@ -107,6 +108,7 @@ type config struct {
RefNoTrack bool RefNoTrack bool
RefNoRef bool RefNoRef bool
NoEmbed bool
Noavatar string // ? - Move this into the settings table? Noavatar string // ? - Move this into the settings table?
ItemsPerPage int // ? - Move this into the settings table? ItemsPerPage int // ? - Move this into the settings table?
@ -163,6 +165,10 @@ func LoadConfig() error {
func ProcessConfig() (err error) { func ProcessConfig() (err error) {
Config.Noavatar = strings.Replace(Config.Noavatar, "{site_url}", Site.URL, -1) Config.Noavatar = strings.Replace(Config.Noavatar, "{site_url}", Site.URL, -1)
guestAvatar = GuestAvatar{buildNoavatar(0, 200), buildNoavatar(0, 48)} guestAvatar = GuestAvatar{buildNoavatar(0, 200), buildNoavatar(0, 48)}
// Strip these unnecessary bits, if we find them.
Site.URL = strings.TrimPrefix(Site.URL, "http://")
Site.URL = strings.TrimPrefix(Site.URL, "https://")
Site.Host = Site.URL Site.Host = Site.URL
Site.LocalHost = Site.Host == "localhost" || Site.Host == "127.0.0.1" || Site.Host == "::1" Site.LocalHost = Site.Host == "localhost" || Site.Host == "127.0.0.1" || Site.Host == "::1"
Site.PortInt, err = strconv.Atoi(Site.Port) Site.PortInt, err = strconv.Atoi(Site.Port)
@ -216,6 +222,9 @@ func ProcessConfig() (err error) {
if Config.LogPruneCutoff == 0 { if Config.LogPruneCutoff == 0 {
Config.LogPruneCutoff = 365 // Default cutoff Config.LogPruneCutoff = 365 // Default cutoff
} }
if Config.NoEmbed {
DefaultParseSettings.NoEmbed = true
}
// ? Find a way of making these unlimited if zero? It might rule out some optimisations, waste memory, and break layouts // ? Find a way of making these unlimited if zero? It might rule out some optimisations, waste memory, and break layouts
if Config.MaxTopicTitleLength == 0 { if Config.MaxTopicTitleLength == 0 {
@ -240,20 +249,18 @@ func ProcessConfig() (err error) {
return nil return nil
} }
func VerifyConfig() error { func VerifyConfig() (err error) {
if !Forums.Exists(Config.DefaultForum) { switch {
return errors.New("Invalid default forum") case !Forums.Exists(Config.DefaultForum):
err = errors.New("Invalid default forum")
case Config.ServerCount < 1:
err = errors.New("You can't have less than one server")
case Config.MaxTopicTitleLength > 100:
err = errors.New("The max topic title length cannot be over 100 as that's unable to fit in the database row")
case Config.MaxUsernameLength > 100:
err = errors.New("The max username length cannot be over 100 as that's unable to fit in the database row")
} }
if Config.ServerCount < 1 { return err
return errors.New("You can't have less than one server")
}
if Config.MaxTopicTitleLength > 100 {
return errors.New("The max topic title length cannot be over 100 as that's unable to fit in the database row")
}
if Config.MaxUsernameLength > 100 {
return errors.New("The max username length cannot be over 100 as that's unable to fit in the database row")
}
return nil
} }
func SwitchToTestDB() { func SwitchToTestDB() {

View File

@ -14,8 +14,8 @@ import (
"github.com/Azareal/Gosora/common/alerts" "github.com/Azareal/Gosora/common/alerts"
p "github.com/Azareal/Gosora/common/phrases" p "github.com/Azareal/Gosora/common/phrases"
"github.com/Azareal/Gosora/common/templates" tmpl "github.com/Azareal/Gosora/common/templates"
"github.com/Azareal/Gosora/query_gen" qgen "github.com/Azareal/Gosora/query_gen"
) )
var Ctemplates []string // TODO: Use this to filter out top level templates we don't need var Ctemplates []string // TODO: Use this to filter out top level templates we don't need
@ -93,14 +93,14 @@ var Template_account_handle = genIntTmpl("account")
func tmplInitUsers() (User, User, User) { func tmplInitUsers() (User, User, User) {
avatar, microAvatar := BuildAvatar(62, "") avatar, microAvatar := BuildAvatar(62, "")
user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", avatar, microAvatar, "", "", "", "", 0, 0, 0, 0,"0.0.0.0.0", "", 0} user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", avatar, microAvatar, "", "", 0, 0, 0, 0, "0.0.0.0.0", "", 0, nil}
// TODO: Do a more accurate level calculation for this? // TODO: Do a more accurate level calculation for this?
avatar, microAvatar = BuildAvatar(1, "") avatar, microAvatar = BuildAvatar(1, "")
user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 58, 1000, 0, 1000, "127.0.0.1", "", 0} user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", 58, 1000, 0, 1000, "127.0.0.1", "", 0, nil}
avatar, microAvatar = BuildAvatar(2, "") avatar, microAvatar = BuildAvatar(2, "")
user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 42, 900, 0, 900, "::1", "", 0} user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", 42, 900, 0, 900, "::1", "", 0, nil}
return user, user2, user3 return user, user2, user3
} }
@ -239,11 +239,11 @@ func compileCommons(c *tmpl.CTemplateSet, head *Header, head2 *Header, forumList
}, VoteCount: 7} }, VoteCount: 7}
avatar, microAvatar := BuildAvatar(62, "") avatar, microAvatar := BuildAvatar(62, "")
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}} miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false, miniAttach, nil,false} topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", 58, false, miniAttach, nil, false}
var replyList []*ReplyUser var replyList []*ReplyUser
reply := Reply{1, 1, "Yo!", 1, Config.DefaultGroup, now, 0, 0, 1, "::1", true, 1, 1, ""} reply := Reply{1, 1, "Yo!", 1, Config.DefaultGroup, now, 0, 0, 1, "::1", true, 1, 1, ""}
ru := &ReplyUser{ClassName: "", Reply: reply, CreatedByName: "Alice", Avatar: avatar, URLPrefix: "", URLName: "", Level: 0, Attachments: miniAttach} ru := &ReplyUser{ClassName: "", Reply: reply, CreatedByName: "Alice", Avatar: avatar, Level: 0, Attachments: miniAttach}
ru.Init() ru.Init()
replyList = append(replyList, ru) replyList = append(replyList, ru)
tpage := TopicPage{htitle("Topic Name"), replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}} tpage := TopicPage{htitle("Topic Name"), replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}}
@ -271,7 +271,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
// TODO: Do we want the UID on this to be 0? // TODO: Do we want the UID on this to be 0?
//avatar, microAvatar = BuildAvatar(0, "") //avatar, microAvatar = BuildAvatar(0, "")
reply := Reply{1, 1, "Yo!", 1, Config.DefaultGroup, now, 0, 0, 1, "::1", true, 1, 1, ""} reply := Reply{1, 1, "Yo!", 1, Config.DefaultGroup, now, 0, 0, 1, "::1", true, 1, 1, ""}
ru := &ReplyUser{ClassName: "", Reply: reply, CreatedByName: "Alice", Avatar: "", URLPrefix: "", URLName: "", Level: 0, Attachments: miniAttach} ru := &ReplyUser{ClassName: "", Reply: reply, CreatedByName: "Alice", Avatar: "", Level: 0, Attachments: miniAttach}
ru.Init() ru.Init()
replyList = append(replyList, ru) replyList = append(replyList, ru)
@ -296,7 +296,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
return err return err
} }
ppage := ProfilePage{htitle("User 526"), replyList, user, 0, 0, false,false,false} // TODO: Use the score from user to generate the currentScore and nextScore ppage := ProfilePage{htitle("User 526"), replyList, user, 0, 0, false, false, false} // TODO: Use the score from user to generate the currentScore and nextScore
t.Add("profile", "c.ProfilePage", ppage) t.Add("profile", "c.ProfilePage", ppage)
var topicsList []*TopicsRow var topicsList []*TopicsRow
@ -307,8 +307,8 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
forumPage := ForumPage{htitle("General Forum"), topicsList, forumItem, Paginator{[]int{1}, 1, 1}} forumPage := ForumPage{htitle("General Forum"), topicsList, forumItem, Paginator{[]int{1}, 1, 1}}
// Experimental! // Experimental!
for _, tmpl := range strings.Split(Dev.ExtraTmpls,",") { for _, tmpl := range strings.Split(Dev.ExtraTmpls, ",") {
sp := strings.Split(tmpl,":") sp := strings.Split(tmpl, ":")
if len(sp) < 2 { if len(sp) < 2 {
continue continue
} }
@ -350,27 +350,27 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
t.AddStd("account", "c.Account", accountPage) t.AddStd("account", "c.Account", accountPage)
parti := []*User{&user} parti := []*User{&user}
convo := &Conversation{1,user.ID,time.Now(),0,time.Now()} convo := &Conversation{1, user.ID, time.Now(), 0, time.Now()}
convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1,1,"hey","",user.ID}, &user, "", 4, true}} convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}} convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}}
t.AddStd("convo", "c.ConvoViewPage", convoPage) t.AddStd("convo", "c.ConvoViewPage", convoPage)
convos := []*ConversationExtra{&ConversationExtra{&Conversation{},[]*User{&user}}} convos := []*ConversationExtra{&ConversationExtra{&Conversation{}, []*User{&user}}}
convoListPage := ConvoListPage{header, convos, Paginator{[]int{1}, 1, 1}} convoListPage := ConvoListPage{header, convos, Paginator{[]int{1}, 1, 1}}
t.AddStd("convos", "c.ConvoListPage", convoListPage) t.AddStd("convos", "c.ConvoListPage", convoListPage)
basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID} basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID}
t.AddStd("panel", "c.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter}) t.AddStd("panel", "c.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter})
ges := []GridElement{GridElement{"","", "", 1, "grid_istat", "", "", ""}} ges := []GridElement{GridElement{"", "", "", 1, "grid_istat", "", "", ""}}
t.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges,ges}) t.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges, ges})
goVersion := runtime.Version() goVersion := runtime.Version()
dbVersion := qgen.Builder.DbVersion() dbVersion := qgen.Builder.DbVersion()
var memStats runtime.MemStats var memStats runtime.MemStats
runtime.ReadMemStats(&memStats) runtime.ReadMemStats(&memStats)
debugCache := DebugPageCache{1, 1, 1, 2, 2, 2, true} debugCache := DebugPageCache{1, 1, 1, 2, 2, 2, true}
debugDatabase := DebugPageDatabase{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} debugDatabase := DebugPageDatabase{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
debugDisk := DebugPageDisk{1,1,1,1,1,1} debugDisk := DebugPageDisk{1, 1, 1, 1, 1, 1}
dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, memStats, debugCache, debugDatabase, debugDisk} dpage := PanelDebugPage{basePage, goVersion, dbVersion, "0s", 1, qgen.Builder.GetAdapter().GetName(), 1, 1, memStats, debugCache, debugDatabase, debugDisk}
t.AddStd("panel_debug", "c.PanelDebugPage", dpage) t.AddStd("panel_debug", "c.PanelDebugPage", dpage)
//t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter}) //t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter})
@ -531,12 +531,12 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
}, VoteCount: 7} }, VoteCount: 7}
avatar, microAvatar := BuildAvatar(62, "") avatar, microAvatar := BuildAvatar(62, "")
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}} miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
topic := TopicUser{1, "blah", "Blah", "Hey there!", 62, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false, miniAttach, nil,false} topic := TopicUser{1, "blah", "Blah", "Hey there!", 62, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "","","", 58, false, miniAttach, nil, false}
var replyList []*ReplyUser var replyList []*ReplyUser
// TODO: Do we really want the UID here to be zero? // TODO: Do we really want the UID here to be zero?
avatar, microAvatar = BuildAvatar(0, "") avatar, microAvatar = BuildAvatar(0, "")
reply := Reply{1, 1, "Yo!", 1, Config.DefaultGroup, now, 0, 0, 1, "::1", true, 1, 1, ""} reply := Reply{1, 1, "Yo!", 1, Config.DefaultGroup, now, 0, 0, 1, "::1", true, 1, 1, ""}
ru := &ReplyUser{ClassName: "", Reply: reply, CreatedByName: "Alice", Avatar: avatar, URLPrefix: "", URLName: "", Level: 0, Attachments: miniAttach} ru := &ReplyUser{ClassName: "", Reply: reply, CreatedByName: "Alice", Avatar: avatar, Level: 0, Attachments: miniAttach}
ru.Init() ru.Init()
replyList = append(replyList, ru) replyList = append(replyList, ru)
@ -554,11 +554,11 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
t.AddStd("topic_c_edit_post", "c.TopicCEditPost", TopicCEditPost{ID: 0, Source: "", Ref: ""}) t.AddStd("topic_c_edit_post", "c.TopicCEditPost", TopicCEditPost{ID: 0, Source: "", Ref: ""})
t.AddStd("topic_c_attach_item", "c.TopicCAttachItem", TopicCAttachItem{ID: 1, ImgSrc: "", Path: "", FullPath: ""}) t.AddStd("topic_c_attach_item", "c.TopicCAttachItem", TopicCAttachItem{ID: 1, ImgSrc: "", Path: "", FullPath: ""})
t.AddStd("topic_c_poll_input", "c.TopicCPollInput", TopicCPollInput{Index:0}) t.AddStd("topic_c_poll_input", "c.TopicCPollInput", TopicCPollInput{Index: 0})
parti := []*User{&user} parti := []*User{&user}
convo := &Conversation{1,user.ID,time.Now(),0,time.Now()} convo := &Conversation{1, user.ID, time.Now(), 0, time.Now()}
convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1,1,"hey","",user.ID}, &user, "", 4, true}} convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}} convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}}
t.AddStd("convo", "c.ConvoViewPage", convoPage) t.AddStd("convo", "c.ConvoViewPage", convoPage)

View File

@ -11,7 +11,6 @@ import (
"html" "html"
"html/template" "html/template"
//"log"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -83,8 +82,8 @@ type TopicUser struct {
ContentHTML string // TODO: Avoid converting this to bytes in templates, particularly if it's long ContentHTML string // TODO: Avoid converting this to bytes in templates, particularly if it's long
Tag string Tag string
URL string URL string
URLPrefix string //URLPrefix string
URLName string //URLName string
Level int Level int
Liked bool Liked bool
@ -215,7 +214,7 @@ func init() {
t := "topics" t := "topics"
topicStmts = TopicStmts{ topicStmts = TopicStmts{
getRids: acc.Select("replies").Columns("rid").Where("tid = ?").Orderby("rid ASC").Limit("?,?").Prepare(), getRids: acc.Select("replies").Columns("rid").Where("tid = ?").Orderby("rid ASC").Limit("?,?").Prepare(),
getReplies: acc.SimpleLeftJoin("replies AS r", "users AS u", "r.rid, r.content, r.createdBy, r.createdAt, r.lastEdit, r.lastEditBy, u.avatar, u.name, u.group, u.url_prefix, u.url_name, u.level, r.ipaddress, r.likeCount, r.attachCount, r.actionType", "r.createdBy = u.uid", "r.tid = ?", "r.rid ASC", "?,?"), getReplies: acc.SimpleLeftJoin("replies AS r", "users AS u", "r.rid, r.content, r.createdBy, r.createdAt, r.lastEdit, r.lastEditBy, u.avatar, u.name, u.group, u.level, r.ipaddress, r.likeCount, r.attachCount, r.actionType", "r.createdBy = u.uid", "r.tid = ?", "r.rid ASC", "?,?"),
addReplies: acc.Update(t).Set("postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()").Where("tid = ?").Prepare(), addReplies: acc.Update(t).Set("postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()").Where("tid = ?").Prepare(),
updateLastReply: acc.Update(t).Set("lastReplyID = ?").Where("lastReplyID > ? AND tid = ?").Prepare(), updateLastReply: acc.Update(t).Set("lastReplyID = ?").Where("lastReplyID > ? AND tid = ?").Prepare(),
lock: acc.Update(t).Set("is_closed = 1").Where("tid = ?").Prepare(), lock: acc.Update(t).Set("is_closed = 1").Where("tid = ?").Prepare(),
@ -233,7 +232,7 @@ func init() {
setPoll: acc.Update(t).Set("poll = ?").Where("tid = ? AND poll = 0").Prepare(), setPoll: acc.Update(t).Set("poll = ?").Where("tid = ? AND poll = 0").Prepare(),
createAction: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(), createAction: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
getTopicUser: acc.SimpleLeftJoin("topics AS t", "users AS u", "t.title, t.content, t.createdBy, t.createdAt, t.lastReplyAt, t.lastReplyBy, t.lastReplyID, t.is_closed, t.sticky, t.parentID, t.ipaddress, t.views, t.postCount, t.likeCount, t.attachCount,t.poll, u.name, u.avatar, u.group, u.url_prefix, u.url_name, u.level", "t.createdBy = u.uid", "tid = ?", "", ""), getTopicUser: acc.SimpleLeftJoin("topics AS t", "users AS u", "t.title, t.content, t.createdBy, t.createdAt, t.lastReplyAt, t.lastReplyBy, t.lastReplyID, t.is_closed, t.sticky, t.parentID, t.ipaddress, t.views, t.postCount, t.likeCount, t.attachCount,t.poll, u.name, u.avatar, u.group, u.level", "t.createdBy = u.uid", "tid = ?", "", ""),
getByReplyID: acc.SimpleLeftJoin("replies AS r", "topics AS t", "t.tid, t.title, t.content, t.createdBy, t.createdAt, t.is_closed, t.sticky, t.parentID, t.ipaddress, t.views, t.postCount, t.likeCount, t.poll, t.data", "r.tid = t.tid", "rid = ?", "", ""), getByReplyID: acc.SimpleLeftJoin("replies AS r", "topics AS t", "t.tid, t.title, t.content, t.createdBy, t.createdAt, t.is_closed, t.sticky, t.parentID, t.ipaddress, t.views, t.postCount, t.likeCount, t.poll, t.data", "r.tid = t.tid", "rid = ?", "", ""),
} }
return acc.FirstError() return acc.FirstError()
@ -250,7 +249,7 @@ func (t *Topic) cacheRemove() {
} }
// TODO: Write a test for this // TODO: Write a test for this
func (t *Topic) AddReply(rid int, uid int) (err error) { func (t *Topic) AddReply(rid, uid int) (err error) {
_, err = topicStmts.addReplies.Exec(1, uid, t.ID) _, err = topicStmts.addReplies.Exec(1, uid, t.ID)
if err != nil { if err != nil {
return err return err
@ -300,7 +299,7 @@ func (t *Topic) Unstick() (err error) {
// TODO: Test this // TODO: Test this
// TODO: Use a transaction for this // TODO: Use a transaction for this
func (t *Topic) Like(score int, uid int) (err error) { func (t *Topic) Like(score, uid int) (err error) {
var disp int // Unused var disp int // Unused
err = topicStmts.hasLikedTopic.QueryRow(uid, t.ID).Scan(&disp) err = topicStmts.hasLikedTopic.QueryRow(uid, t.ID).Scan(&disp)
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
@ -308,12 +307,10 @@ func (t *Topic) Like(score int, uid int) (err error) {
} else if err != ErrNoRows { } else if err != ErrNoRows {
return ErrAlreadyLiked return ErrAlreadyLiked
} }
_, err = topicStmts.createLike.Exec(score, t.ID, "topics", uid) _, err = topicStmts.createLike.Exec(score, t.ID, "topics", uid)
if err != nil { if err != nil {
return err return err
} }
_, err = topicStmts.addLikesToTopic.Exec(1, t.ID) _, err = topicStmts.addLikesToTopic.Exec(1, t.ID)
if err != nil { if err != nil {
return err return err
@ -360,7 +357,7 @@ func (t *Topic) Delete() error {
} }
// TODO: Write tests for this // TODO: Write tests for this
func (t *Topic) Update(name string, content string) error { func (t *Topic) Update(name, content string) error {
name = SanitiseSingleLine(html.UnescapeString(name)) name = SanitiseSingleLine(html.UnescapeString(name))
if name == "" { if name == "" {
return ErrNoTitle return ErrNoTitle
@ -371,7 +368,7 @@ func (t *Topic) Update(name string, content string) error {
} }
content = PreparseMessage(html.UnescapeString(content)) content = PreparseMessage(html.UnescapeString(content))
parsedContent := ParseMessage(content, t.ParentID, "forums") parsedContent := ParseMessage(content, t.ParentID, "forums", nil)
_, err := topicStmts.edit.Exec(name, content, parsedContent, t.ID) _, err := topicStmts.edit.Exec(name, content, parsedContent, t.ID)
t.cacheRemove() t.cacheRemove()
return err return err
@ -404,7 +401,7 @@ func (t *Topic) CreateActionReply(action string, ip string, uid int) (err error)
return err return err
} }
func GetRidsForTopic(tid int, offset int) (rids []int, err error) { func GetRidsForTopic(tid, offset int) (rids []int, err error) {
rows, err := topicStmts.getRids.Query(tid, offset, Config.ItemsPerPage) rows, err := topicStmts.getRids.Query(tid, offset, Config.ItemsPerPage)
if err != nil { if err != nil {
return nil, err return nil, err
@ -450,20 +447,16 @@ func (ru *ReplyUser) Init() error {
// We really shouldn't have inline HTML, we should do something about this... // We really shouldn't have inline HTML, we should do something about this...
if ru.ActionType != "" { if ru.ActionType != "" {
var action string
aarr := strings.Split(ru.ActionType, "-") aarr := strings.Split(ru.ActionType, "-")
switch aarr[0] { action := aarr[0]
switch action {
case "lock": case "lock":
action = aarr[0]
ru.ActionIcon = lockai ru.ActionIcon = lockai
case "unlock": case "unlock":
action = aarr[0]
ru.ActionIcon = unlockai ru.ActionIcon = unlockai
case "stick": case "stick":
action = aarr[0]
ru.ActionIcon = stickai ru.ActionIcon = stickai
case "unstick": case "unstick":
action = aarr[0]
ru.ActionIcon = unstickai ru.ActionIcon = unstickai
case "move": case "move":
if len(aarr) == 2 { if len(aarr) == 2 {
@ -471,20 +464,16 @@ func (ru *ReplyUser) Init() error {
forum, err := Forums.Get(fid) forum, err := Forums.Get(fid)
if err == nil { if err == nil {
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_move_dest", forum.Link, forum.Name, ru.UserLink, ru.CreatedByName) ru.ActionType = p.GetTmplPhrasef("topic.action_topic_move_dest", forum.Link, forum.Name, ru.UserLink, ru.CreatedByName)
} else { return nil
action = aarr[0]
} }
} else {
action = aarr[0]
} }
default: default:
// TODO: Only fire this off if a corresponding phrase for the ActionType doesn't exist? Or maybe have some sort of action registry? // TODO: Only fire this off if a corresponding phrase for the ActionType doesn't exist? Or maybe have some sort of action registry?
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_default", ru.ActionType) ru.ActionType = p.GetTmplPhrasef("topic.action_topic_default", ru.ActionType)
return nil
} }
if action != "" {
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_"+action, ru.UserLink, ru.CreatedByName) ru.ActionType = p.GetTmplPhrasef("topic.action_topic_"+action, ru.UserLink, ru.CreatedByName)
} }
}
return nil return nil
} }
@ -516,42 +505,52 @@ func (t *TopicUser) Replies(offset int, pFrag int, user *User) (rlist []*ReplyUs
ruser, err = ucache.Get(re.CreatedBy) ruser, err = ucache.Get(re.CreatedBy)
} }
// TODO: Factor the user fields out and embed a user struct instead
var reply *ReplyUser
hTbl := GetHookTable() hTbl := GetHookTable()
if err == nil { rf := func(r *ReplyUser) error {
//log.Print("reply cached serve") err := r.Init()
reply = &ReplyUser{ClassName: "", Reply: *re, CreatedByName: ruser.Name, Avatar: ruser.Avatar, URLPrefix: ruser.URLPrefix, URLName: ruser.URLName, Level: ruser.Level}
err := reply.Init()
if err != nil { if err != nil {
return nil, "", err return err
}
reply.ContentHtml = ParseMessage(reply.Content, t.ParentID, "forums")
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
if reply.ContentHtml == reply.Content {
reply.ContentHtml = reply.Content
} }
if reply.ID == pFrag { r.ContentHtml = ParseMessage(r.Content, t.ParentID, "forums", user.ParseSettings)
ogdesc = reply.Content // TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
if r.ContentHtml == r.Content {
r.ContentHtml = r.Content
}
// TODO: This doesn't work properly so pick the first one instead?
if r.ID == pFrag {
ogdesc = r.Content
if len(ogdesc) > 200 { if len(ogdesc) > 200 {
ogdesc = ogdesc[:197] + "..." ogdesc = ogdesc[:197] + "..."
} }
} }
if reply.LikeCount > 0 && user.Liked > 0 { if r.LikeCount > 0 && user.Liked > 0 {
likedMap[reply.ID] = len(rlist) likedMap[r.ID] = len(rlist)
likedQueryList = append(likedQueryList, reply.ID) likedQueryList = append(likedQueryList, r.ID)
} }
if user.Perms.EditReply && reply.AttachCount > 0 { if user.Perms.EditReply && r.AttachCount > 0 {
attachMap[reply.ID] = len(rlist) attachMap[r.ID] = len(rlist)
attachQueryList = append(attachQueryList, reply.ID) attachQueryList = append(attachQueryList, r.ID)
} }
reply.Deletable = user.Perms.DeleteReply || reply.CreatedBy == user.ID r.Deletable = user.Perms.DeleteReply || r.CreatedBy == user.ID
hTbl.VhookNoRet("topic_reply_row_assign", &rlist, &reply) hTbl.VhookNoRet("topic_reply_row_assign", &rlist, &r)
rlist = append(rlist, reply) // TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
rlist = append(rlist, r)
//log.Printf("r: %d-%d", r.ID, len(rlist)-1)
return nil
}
// TODO: Factor the user fields out and embed a user struct instead
if err == nil {
//log.Print("reply cached serve")
reply := &ReplyUser{ClassName: "", Reply: *re, CreatedByName: ruser.Name, Avatar: ruser.Avatar /*URLPrefix: ruser.URLPrefix, URLName: ruser.URLName, */, Level: ruser.Level}
err = rf(reply)
if err != nil {
return nil, "", err
}
} else { } else {
rows, err := topicStmts.getReplies.Query(t.ID, offset, Config.ItemsPerPage) rows, err := topicStmts.getReplies.Query(t.ID, offset, Config.ItemsPerPage)
if err != nil { if err != nil {
@ -560,39 +559,15 @@ func (t *TopicUser) Replies(offset int, pFrag int, user *User) (rlist []*ReplyUs
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
reply = &ReplyUser{} r := &ReplyUser{}
err := rows.Scan(&reply.ID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.Avatar, &reply.CreatedByName, &reply.Group, &reply.URLPrefix, &reply.URLName, &reply.Level, &reply.IP, &reply.LikeCount, &reply.AttachCount, &reply.ActionType) err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &r.Group /*&r.URLPrefix, &r.URLName,*/, &r.Level, &r.IP, &r.LikeCount, &r.AttachCount, &r.ActionType)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
if err := reply.Init(); err != nil { err = rf(r)
if err != nil {
return nil, "", err return nil, "", err
} }
reply.ContentHtml = ParseMessage(reply.Content, t.ParentID, "forums")
// TODO: This doesn't work properly so pick the first one instead?
if reply.ID == pFrag {
ogdesc = reply.Content
if len(ogdesc) > 200 {
ogdesc = ogdesc[:197] + "..."
}
}
if reply.LikeCount > 0 && user.Liked > 0 {
likedMap[reply.ID] = len(rlist)
likedQueryList = append(likedQueryList, reply.ID)
}
if user.Perms.EditReply && reply.AttachCount > 0 {
attachMap[reply.ID] = len(rlist)
attachQueryList = append(attachQueryList, reply.ID)
}
reply.Deletable = user.Perms.DeleteReply || reply.CreatedBy == user.ID
hTbl.VhookNoRet("topic_reply_row_assign", &rlist, &reply)
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
rlist = append(rlist, reply)
//log.Printf("r: %d-%d", reply.ID, len(rlist)-1)
} }
err = rows.Err() err = rows.Err()
if err != nil { if err != nil {
@ -689,7 +664,7 @@ func GetTopicUser(user *User, tid int) (tu TopicUser, err error) {
tu = TopicUser{ID: tid} tu = TopicUser{ID: tid}
// TODO: This misses some important bits... // TODO: This misses some important bits...
err = topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.LastReplyAt, &tu.LastReplyBy, &tu.LastReplyID, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.AttachCount, &tu.Poll, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) err = topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.LastReplyAt, &tu.LastReplyBy, &tu.LastReplyID, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.AttachCount, &tu.Poll, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
tu.Avatar, tu.MicroAvatar = BuildAvatar(tu.CreatedBy, tu.Avatar) tu.Avatar, tu.MicroAvatar = BuildAvatar(tu.CreatedBy, tu.Avatar)
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID) tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy) tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy)
@ -709,8 +684,8 @@ func copyTopicToTopicUser(t *Topic, u *User) (tu TopicUser) {
tu.Group = u.Group tu.Group = u.Group
tu.Avatar = u.Avatar tu.Avatar = u.Avatar
tu.MicroAvatar = u.MicroAvatar tu.MicroAvatar = u.MicroAvatar
tu.URLPrefix = u.URLPrefix //tu.URLPrefix = u.URLPrefix
tu.URLName = u.URLName //tu.URLName = u.URLName
tu.Level = u.Level tu.Level = u.Level
tu.ID = t.ID tu.ID = t.ID

View File

@ -29,7 +29,7 @@ type TopicStore interface {
BypassGet(id int) (*Topic, error) BypassGet(id int) (*Topic, error)
BulkGetMap(ids []int) (list map[int]*Topic, err error) BulkGetMap(ids []int) (list map[int]*Topic, err error)
Exists(id int) bool Exists(id int) bool
Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) Create(fid int, name string, content string, uid int, ip string) (tid int, err error)
AddLastTopic(item *Topic, fid int) error // unimplemented AddLastTopic(item *Topic, fid int) error // unimplemented
Reload(id int) error // Too much SQL logic to move into TopicCache Reload(id int) error // Too much SQL logic to move into TopicCache
// TODO: Implement these two methods // TODO: Implement these two methods
@ -197,22 +197,22 @@ func (s *DefaultTopicStore) Exists(id int) bool {
return s.exists.QueryRow(id).Scan(&id) == nil return s.exists.QueryRow(id).Scan(&id) == nil
} }
func (s *DefaultTopicStore) Create(fid int, topicName string, content string, uid int, ip string) (tid int, err error) { func (s *DefaultTopicStore) Create(fid int, name string, content string, uid int, ip string) (tid int, err error) {
if topicName == "" { if name == "" {
return 0, ErrNoTitle return 0, ErrNoTitle
} }
// ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory? // ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory?
if len(topicName) > Config.MaxTopicTitleLength { if len(name) > Config.MaxTopicTitleLength {
return 0, ErrLongTitle return 0, ErrLongTitle
} }
parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums")) parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums", nil))
if parsedContent == "" { if parsedContent == "" {
return 0, ErrNoBody return 0, ErrNoBody
} }
// TODO: Move this statement into the topic store // TODO: Move this statement into the topic store
res, err := s.create.Exec(fid, topicName, content, parsedContent, uid, ip, WordCount(content), uid) res, err := s.create.Exec(fid, name, content, parsedContent, uid, ip, WordCount(content), uid)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -46,8 +46,9 @@ type User struct {
Avatar string Avatar string
MicroAvatar string MicroAvatar string
Message string Message string
URLPrefix string // Move this to another table? Create a user lite? // TODO: Implement something like this for profiles?
URLName string //URLPrefix string // Move this to another table? Create a user lite?
//URLName string
Tag string Tag string
Level int Level int
Score int Score int
@ -56,6 +57,8 @@ type User struct {
LastIP string // ! This part of the UserCache data might fall out of date LastIP string // ! This part of the UserCache data might fall out of date
LastAgent string // ! Temporary hack, don't use LastAgent string // ! Temporary hack, don't use
TempGroup int TempGroup int
ParseSettings *ParseSettings
} }
func (u *User) WebSockets() *WsJSONUser { func (u *User) WebSockets() *WsJSONUser {
@ -119,7 +122,7 @@ type UserStmts struct {
changeGroup *sql.Stmt changeGroup *sql.Stmt
delete *sql.Stmt delete *sql.Stmt
setAvatar *sql.Stmt setAvatar *sql.Stmt
setUsername *sql.Stmt setName *sql.Stmt
incTopics *sql.Stmt incTopics *sql.Stmt
updateLevel *sql.Stmt updateLevel *sql.Stmt
update *sql.Stmt update *sql.Stmt
@ -133,6 +136,7 @@ type UserStmts struct {
decLiked *sql.Stmt decLiked *sql.Stmt
updateLastIP *sql.Stmt updateLastIP *sql.Stmt
updatePrivacy *sql.Stmt
setPassword *sql.Stmt setPassword *sql.Stmt
@ -143,27 +147,29 @@ var userStmts UserStmts
func init() { func init() {
DbInits.Add(func(acc *qgen.Accumulator) error { DbInits.Add(func(acc *qgen.Accumulator) error {
w := "uid = ?" u := "users"
w := "uid=?"
userStmts = UserStmts{ userStmts = UserStmts{
activate: acc.SimpleUpdate("users", "active = 1", w), activate: acc.SimpleUpdate(u, "active=1", w),
changeGroup: acc.SimpleUpdate("users", "group = ?", w), // TODO: Implement user_count for users_groups here changeGroup: acc.SimpleUpdate(u, "group=?", w), // TODO: Implement user_count for users_groups here
delete: acc.SimpleDelete("users", w), delete: acc.Delete(u).Where(w).Prepare(),
setAvatar: acc.Update("users").Set("avatar = ?").Where(w).Prepare(), setAvatar: acc.Update(u).Set("avatar=?").Where(w).Prepare(),
setUsername: acc.Update("users").Set("name = ?").Where(w).Prepare(), setName: acc.Update(u).Set("name=?").Where(w).Prepare(),
incTopics: acc.SimpleUpdate("users", "topics = topics + ?", w), incTopics: acc.SimpleUpdate(u, "topics=topics+?", w),
updateLevel: acc.SimpleUpdate("users", "level = ?", w), updateLevel: acc.SimpleUpdate(u, "level=?", w),
update: acc.Update("users").Set("name = ?, email = ?, group = ?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this update: acc.Update(u).Set("name=?,email=?,group=?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this
incScore: acc.Update("users").Set("score = score + ?").Where(w).Prepare(), incScore: acc.Update(u).Set("score=score+?").Where(w).Prepare(),
incPosts: acc.Update("users").Set("posts = posts + ?").Where(w).Prepare(), incPosts: acc.Update(u).Set("posts=posts+?").Where(w).Prepare(),
incBigposts: acc.Update("users").Set("posts = posts + ?, bigposts = bigposts + ?").Where(w).Prepare(), incBigposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?").Where(w).Prepare(),
incMegaposts: acc.Update("users").Set("posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?").Where(w).Prepare(), incMegaposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?,megaposts=megaposts+?").Where(w).Prepare(),
incLiked: acc.Update("users").Set("liked = liked + ?, lastLiked = UTC_TIMESTAMP()").Where(w).Prepare(), incLiked: acc.Update(u).Set("liked=liked+?,lastLiked=UTC_TIMESTAMP()").Where(w).Prepare(),
decLiked: acc.Update("users").Set("liked = liked - ?").Where(w).Prepare(), decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(),
//recalcLastLiked: acc... //recalcLastLiked: acc...
updateLastIP: acc.SimpleUpdate("users", "last_ip = ?", w), updateLastIP: acc.SimpleUpdate(u, "last_ip=?", w),
updatePrivacy: acc.Update(u).Set("enable_embeds=?").Where(w).Prepare(),
setPassword: acc.Update("users").Set("password = ?, salt = ?").Where(w).Prepare(), setPassword: acc.Update(u).Set("password=?,salt=?").Where(w).Prepare(),
scheduleAvatarResize: acc.Insert("users_avatar_queue").Columns("uid").Fields("?").Prepare(), scheduleAvatarResize: acc.Insert("users_avatar_queue").Columns("uid").Fields("?").Prepare(),
} }
@ -204,7 +210,7 @@ func (u *User) deleteScheduleGroupTx(tx *sql.Tx) error {
} }
func (u *User) setTempGroupTx(tx *sql.Tx, tempGroup int) error { func (u *User) setTempGroupTx(tx *sql.Tx, tempGroup int) error {
setTempGroupStmt, err := qgen.Builder.SimpleUpdateTx(tx, "users", "temp_group = ?", "uid = ?") setTempGroupStmt, err := qgen.Builder.SimpleUpdateTx(tx, "users", "temp_group=?", "uid=?")
if err != nil { if err != nil {
return err return err
} }
@ -304,7 +310,7 @@ func (u *User) bindStmt(stmt *sql.Stmt, params ...interface{}) (err error) {
} }
func (u *User) ChangeName(name string) (err error) { func (u *User) ChangeName(name string) (err error) {
return u.bindStmt(userStmts.setUsername, name) return u.bindStmt(userStmts.setName, name)
} }
func (u *User) ChangeAvatar(avatar string) (err error) { func (u *User) ChangeAvatar(avatar string) (err error) {
@ -341,7 +347,15 @@ func (u *User) UpdateIP(host string) error {
return err return err
} }
func (u *User) Update(name string, email string, group int) (err error) { func (u *User) UpdatePrivacy(enableEmbeds int) error {
_, err := userStmts.updatePrivacy.Exec(enableEmbeds, u.ID)
if uc := Users.GetCache(); uc != nil {
uc.Remove(u.ID)
}
return err
}
func (u *User) Update(name, email string, group int) (err error) {
return u.bindStmt(userStmts.update, name, email, group) return u.bindStmt(userStmts.update, name, email, group)
} }
@ -461,7 +475,7 @@ type GuestAvatar struct {
Micro string Micro string
} }
func buildNoavatar(uid int, width int) string { func buildNoavatar(uid, width int) string {
if !Config.DisableNoavatarRange { if !Config.DisableNoavatarRange {
// TODO: Find a faster algorithm // TODO: Find a faster algorithm
if uid > 50000 { if uid > 50000 {
@ -520,7 +534,6 @@ func wordsToScore(wcount int, topic bool) (score int) {
} else { } else {
score = 1 score = 1
} }
settings := SettingBox.Load().(SettingMap) settings := SettingBox.Load().(SettingMap)
if wcount >= settings["megapost_min_words"].(int) { if wcount >= settings["megapost_min_words"].(int) {
score += 4 score += 4

View File

@ -5,7 +5,7 @@ import (
"errors" "errors"
"strconv" "strconv"
"github.com/Azareal/Gosora/query_gen" qgen "github.com/Azareal/Gosora/query_gen"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@ -20,11 +20,11 @@ type UserStore interface {
Get(id int) (*User, error) Get(id int) (*User, error)
GetByName(name string) (*User, error) GetByName(name string) (*User, error)
Exists(id int) bool Exists(id int) bool
GetOffset(offset int, perPage int) (users []*User, err error) GetOffset(offset, perPage int) ([]*User, error)
//BulkGet(ids []int) ([]*User, error) //BulkGet(ids []int) ([]*User, error)
BulkGetMap(ids []int) (map[int]*User, error) BulkGetMap(ids []int) (map[int]*User, error)
BypassGet(id int) (*User, error) BypassGet(id int) (*User, error)
Create(username string, password string, email string, group int, active bool) (int, error) Create(name string, password string, email string, group int, active bool) (int, error)
Reload(id int) error Reload(id int) error
Count() int Count() int
@ -40,7 +40,7 @@ type DefaultUserStore struct {
getOffset *sql.Stmt getOffset *sql.Stmt
exists *sql.Stmt exists *sql.Stmt
register *sql.Stmt register *sql.Stmt
usernameExists *sql.Stmt nameExists *sql.Stmt
count *sql.Stmt count *sql.Stmt
} }
@ -50,16 +50,17 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
if cache == nil { if cache == nil {
cache = NewNullUserCache() cache = NewNullUserCache()
} }
u := "users"
// TODO: Add an admin version of registerStmt with more flexibility? // TODO: Add an admin version of registerStmt with more flexibility?
return &DefaultUserStore{ return &DefaultUserStore{
cache: cache, cache: cache,
get: acc.SimpleSelect("users", "name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, posts, liked, last_ip, temp_group", "uid = ?", "", ""), get: acc.Select(u).Columns("name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Where("uid = ?").Prepare(),
getByName: acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, posts, liked, last_ip, temp_group").Where("name = ?").Prepare(), getByName: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Where("name = ?").Prepare(),
getOffset: acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, posts, liked, last_ip, temp_group").Orderby("uid ASC").Limit("?,?").Prepare(), getOffset: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Orderby("uid ASC").Limit("?,?").Prepare(),
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""), exists: acc.Exists(u, "uid").Prepare(),
register: acc.Insert("users").Columns("name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here register: acc.Insert(u).Columns("name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here
usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""), nameExists: acc.Exists(u, "name").Prepare(),
count: acc.Count("users").Prepare(), count: acc.Count(u).Prepare(),
}, acc.FirstError() }, acc.FirstError()
} }
@ -86,8 +87,13 @@ func (s *DefaultUserStore) Get(id int) (*User, error) {
//log.Print("uncached user") //log.Print("uncached user")
u = &User{ID: id, Loggedin: true} u = &User{ID: id, Loggedin: true}
err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts,&u.Liked, &u.LastIP, &u.TempGroup) var embeds int
err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &embeds)
if err == nil { if err == nil {
if embeds != -1 {
u.ParseSettings = DefaultParseSettings.CopyPtr()
u.ParseSettings.NoEmbed = embeds == 0
}
u.Init() u.Init()
s.cache.Set(u) s.cache.Set(u)
} }
@ -98,8 +104,13 @@ func (s *DefaultUserStore) Get(id int) (*User, error) {
// ! This bypasses the cache, use frugally // ! This bypasses the cache, use frugally
func (s *DefaultUserStore) GetByName(name string) (*User, error) { func (s *DefaultUserStore) GetByName(name string) (*User, error) {
u := &User{Loggedin: true} u := &User{Loggedin: true}
err := s.getByName.QueryRow(name).Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts,&u.Liked, &u.LastIP, &u.TempGroup) var embeds int
err := s.getByName.QueryRow(name).Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &embeds)
if err == nil { if err == nil {
if embeds != -1 {
u.ParseSettings = DefaultParseSettings.CopyPtr()
u.ParseSettings.NoEmbed = embeds == 0
}
u.Init() u.Init()
s.cache.Set(u) s.cache.Set(u)
} }
@ -115,12 +126,17 @@ func (s *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User, er
} }
defer rows.Close() defer rows.Close()
var embeds int
for rows.Next() { for rows.Next() {
u := &User{Loggedin: true} u := &User{Loggedin: true}
err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup) err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &embeds)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if embeds != -1 {
u.ParseSettings = DefaultParseSettings.CopyPtr()
u.ParseSettings.NoEmbed = embeds == 0
}
u.Init() u.Init()
s.cache.Set(u) s.cache.Set(u)
users = append(users, u) users = append(users, u)
@ -164,31 +180,35 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error)
// TODO: Add a function for the q stuff // TODO: Add a function for the q stuff
var q string var q string
idList := make([]interface{},len(ids)) idList := make([]interface{}, len(ids))
for i, id := range ids { for i, id := range ids {
idList[i] = strconv.Itoa(id) idList[i] = strconv.Itoa(id)
q += "?," q += "?,"
} }
q = q[0 : len(q)-1] q = q[0 : len(q)-1]
rows, err := qgen.NewAcc().Select("users").Columns("uid,name,group,active,is_super_admin,session,email,avatar,message,url_prefix,url_name,level,score,posts,liked,last_ip,temp_group").Where("uid IN(" + q + ")").Query(idList...) rows, err := qgen.NewAcc().Select("users").Columns("uid,name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,enable_embeds").Where("uid IN(" + q + ")").Query(idList...)
if err != nil { if err != nil {
return list, err return list, err
} }
defer rows.Close() defer rows.Close()
var embeds int
for rows.Next() { for rows.Next() {
u := &User{Loggedin: true} u := &User{Loggedin: true}
err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup) err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &embeds)
if err != nil { if err != nil {
return list, err return list, err
} }
if embeds != -1 {
u.ParseSettings = DefaultParseSettings.CopyPtr()
u.ParseSettings.NoEmbed = embeds == 0
}
u.Init() u.Init()
s.cache.Set(u) s.cache.Set(u)
list[u.ID] = u list[u.ID] = u
} }
err = rows.Err() if err = rows.Err(); err != nil {
if err != nil {
return list, err return list, err
} }
@ -212,8 +232,13 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error)
func (s *DefaultUserStore) BypassGet(id int) (*User, error) { func (s *DefaultUserStore) BypassGet(id int) (*User, error) {
u := &User{ID: id, Loggedin: true} u := &User{ID: id, Loggedin: true}
err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup) var embeds int
err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &embeds)
if err == nil { if err == nil {
if embeds != -1 {
u.ParseSettings = DefaultParseSettings.CopyPtr()
u.ParseSettings.NoEmbed = embeds == 0
}
u.Init() u.Init()
} }
return u, err return u, err
@ -240,16 +265,16 @@ func (s *DefaultUserStore) Exists(id int) bool {
// TODO: Change active to a bool? // TODO: Change active to a bool?
// TODO: Use unique keys for the usernames // TODO: Use unique keys for the usernames
func (s *DefaultUserStore) Create(username string, password string, email string, group int, active bool) (int, error) { func (s *DefaultUserStore) Create(name string, password string, email string, group int, active bool) (int, error) {
// TODO: Strip spaces? // TODO: Strip spaces?
// ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory? // ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory?
if len(username) > Config.MaxUsernameLength { if len(name) > Config.MaxUsernameLength {
return 0, ErrLongUsername return 0, ErrLongUsername
} }
// Is this username already taken..? // Is this name already taken..?
err := s.usernameExists.QueryRow(username).Scan(&username) err := s.nameExists.QueryRow(name).Scan(&name)
if err != ErrNoRows { if err != ErrNoRows {
return 0, ErrAccountExists return 0, ErrAccountExists
} }
@ -262,7 +287,7 @@ func (s *DefaultUserStore) Create(username string, password string, email string
return 0, err return 0, err
} }
res, err := s.register.Exec(username, email, string(hashedPassword), salt, group, active) res, err := s.register.Exec(name, email, string(hashedPassword), salt, group, active)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -31,12 +31,12 @@ type Version struct {
} }
// TODO: Write a test for this // TODO: Write a test for this
func (version *Version) String() (out string) { func (ver *Version) String() (out string) {
out = strconv.Itoa(version.Major) + "." + strconv.Itoa(version.Minor) + "." + strconv.Itoa(version.Patch) out = strconv.Itoa(ver.Major) + "." + strconv.Itoa(ver.Minor) + "." + strconv.Itoa(ver.Patch)
if version.Tag != "" { if ver.Tag != "" {
out += "-" + version.Tag out += "-" + ver.Tag
if version.TagID != 0 { if ver.TagID != 0 {
out += strconv.Itoa(version.TagID) out += strconv.Itoa(ver.TagID)
} }
} }
return return
@ -335,10 +335,10 @@ func HasSuspiciousEmail(email string) bool {
return dotCount > 7 || shortBits > 2 return dotCount > 7 || shortBits > 2
} }
var weakPassStrings = []string{"test", "123","6969","password", "qwerty", "fuck", "love"} var weakPassStrings = []string{"test", "123", "6969", "password", "qwerty", "fuck", "love"}
// TODO: Write a test for this // TODO: Write a test for this
func WeakPassword(password string, username string, email string) error { func WeakPassword(password, username, email string) error {
lowPassword := strings.ToLower(password) lowPassword := strings.ToLower(password)
switch { switch {
case password == "": case password == "":
@ -422,7 +422,7 @@ func createFile(name string) error {
} }
// TODO: Write a test for this // TODO: Write a test for this
func writeFile(name string, content string) (err error) { func writeFile(name, content string) (err error) {
f, err := os.Create(name) f, err := os.Create(name)
if err != nil { if err != nil {
return err return err

View File

@ -110,6 +110,8 @@ RefNoTrack - This switch disables tracking the referrers of users who click from
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. 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.
NoEmbed - Avoid expanding links into videos or images. Default: false
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 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. ItemsPerPage - The number of posts, topics, etc. you want on each page.

View File

@ -373,7 +373,6 @@ func markdownSkipList(data string, index int) int {
goto SkipListInnerLoop goto SkipListInnerLoop
} }
} }
if index >= datalen { if index >= datalen {
if data[index] != '*' && data[index] != '-' { if data[index] != '*' && data[index] != '-' {
if (lastNewline + 1) < datalen { if (lastNewline + 1) < datalen {

View File

@ -121,6 +121,8 @@ var RouteMap = map[string]interface{}{
"routes.AccountEditAvatarSubmit": routes.AccountEditAvatarSubmit, "routes.AccountEditAvatarSubmit": routes.AccountEditAvatarSubmit,
"routes.AccountEditRevokeAvatarSubmit": routes.AccountEditRevokeAvatarSubmit, "routes.AccountEditRevokeAvatarSubmit": routes.AccountEditRevokeAvatarSubmit,
"routes.AccountEditUsernameSubmit": routes.AccountEditUsernameSubmit, "routes.AccountEditUsernameSubmit": routes.AccountEditUsernameSubmit,
"routes.AccountEditPrivacy": routes.AccountEditPrivacy,
"routes.AccountEditPrivacySubmit": routes.AccountEditPrivacySubmit,
"routes.AccountEditMFA": routes.AccountEditMFA, "routes.AccountEditMFA": routes.AccountEditMFA,
"routes.AccountEditMFASetup": routes.AccountEditMFASetup, "routes.AccountEditMFASetup": routes.AccountEditMFASetup,
"routes.AccountEditMFASetupSubmit": routes.AccountEditMFASetupSubmit, "routes.AccountEditMFASetupSubmit": routes.AccountEditMFASetupSubmit,
@ -290,73 +292,75 @@ var routeMapEnum = map[string]int{
"routes.AccountEditAvatarSubmit": 95, "routes.AccountEditAvatarSubmit": 95,
"routes.AccountEditRevokeAvatarSubmit": 96, "routes.AccountEditRevokeAvatarSubmit": 96,
"routes.AccountEditUsernameSubmit": 97, "routes.AccountEditUsernameSubmit": 97,
"routes.AccountEditMFA": 98, "routes.AccountEditPrivacy": 98,
"routes.AccountEditMFASetup": 99, "routes.AccountEditPrivacySubmit": 99,
"routes.AccountEditMFASetupSubmit": 100, "routes.AccountEditMFA": 100,
"routes.AccountEditMFADisableSubmit": 101, "routes.AccountEditMFASetup": 101,
"routes.AccountEditEmail": 102, "routes.AccountEditMFASetupSubmit": 102,
"routes.AccountEditEmailTokenSubmit": 103, "routes.AccountEditMFADisableSubmit": 103,
"routes.AccountLogins": 104, "routes.AccountEditEmail": 104,
"routes.AccountBlocked": 105, "routes.AccountEditEmailTokenSubmit": 105,
"routes.LevelList": 106, "routes.AccountLogins": 106,
"routes.Convos": 107, "routes.AccountBlocked": 107,
"routes.ConvosCreate": 108, "routes.LevelList": 108,
"routes.Convo": 109, "routes.Convos": 109,
"routes.ConvosCreateSubmit": 110, "routes.ConvosCreate": 110,
"routes.ConvosCreateReplySubmit": 111, "routes.Convo": 111,
"routes.ConvosDeleteReplySubmit": 112, "routes.ConvosCreateSubmit": 112,
"routes.ConvosEditReplySubmit": 113, "routes.ConvosCreateReplySubmit": 113,
"routes.RelationsBlockCreate": 114, "routes.ConvosDeleteReplySubmit": 114,
"routes.RelationsBlockCreateSubmit": 115, "routes.ConvosEditReplySubmit": 115,
"routes.RelationsBlockRemove": 116, "routes.RelationsBlockCreate": 116,
"routes.RelationsBlockRemoveSubmit": 117, "routes.RelationsBlockCreateSubmit": 117,
"routes.ViewProfile": 118, "routes.RelationsBlockRemove": 118,
"routes.BanUserSubmit": 119, "routes.RelationsBlockRemoveSubmit": 119,
"routes.UnbanUser": 120, "routes.ViewProfile": 120,
"routes.ActivateUser": 121, "routes.BanUserSubmit": 121,
"routes.IPSearch": 122, "routes.UnbanUser": 122,
"routes.CreateTopicSubmit": 123, "routes.ActivateUser": 123,
"routes.EditTopicSubmit": 124, "routes.IPSearch": 124,
"routes.DeleteTopicSubmit": 125, "routes.CreateTopicSubmit": 125,
"routes.StickTopicSubmit": 126, "routes.EditTopicSubmit": 126,
"routes.UnstickTopicSubmit": 127, "routes.DeleteTopicSubmit": 127,
"routes.LockTopicSubmit": 128, "routes.StickTopicSubmit": 128,
"routes.UnlockTopicSubmit": 129, "routes.UnstickTopicSubmit": 129,
"routes.MoveTopicSubmit": 130, "routes.LockTopicSubmit": 130,
"routes.LikeTopicSubmit": 131, "routes.UnlockTopicSubmit": 131,
"routes.AddAttachToTopicSubmit": 132, "routes.MoveTopicSubmit": 132,
"routes.RemoveAttachFromTopicSubmit": 133, "routes.LikeTopicSubmit": 133,
"routes.ViewTopic": 134, "routes.AddAttachToTopicSubmit": 134,
"routes.CreateReplySubmit": 135, "routes.RemoveAttachFromTopicSubmit": 135,
"routes.ReplyEditSubmit": 136, "routes.ViewTopic": 136,
"routes.ReplyDeleteSubmit": 137, "routes.CreateReplySubmit": 137,
"routes.ReplyLikeSubmit": 138, "routes.ReplyEditSubmit": 138,
"routes.AddAttachToReplySubmit": 139, "routes.ReplyDeleteSubmit": 139,
"routes.RemoveAttachFromReplySubmit": 140, "routes.ReplyLikeSubmit": 140,
"routes.ProfileReplyCreateSubmit": 141, "routes.AddAttachToReplySubmit": 141,
"routes.ProfileReplyEditSubmit": 142, "routes.RemoveAttachFromReplySubmit": 142,
"routes.ProfileReplyDeleteSubmit": 143, "routes.ProfileReplyCreateSubmit": 143,
"routes.PollVote": 144, "routes.ProfileReplyEditSubmit": 144,
"routes.PollResults": 145, "routes.ProfileReplyDeleteSubmit": 145,
"routes.AccountLogin": 146, "routes.PollVote": 146,
"routes.AccountRegister": 147, "routes.PollResults": 147,
"routes.AccountLogout": 148, "routes.AccountLogin": 148,
"routes.AccountLoginSubmit": 149, "routes.AccountRegister": 149,
"routes.AccountLoginMFAVerify": 150, "routes.AccountLogout": 150,
"routes.AccountLoginMFAVerifySubmit": 151, "routes.AccountLoginSubmit": 151,
"routes.AccountRegisterSubmit": 152, "routes.AccountLoginMFAVerify": 152,
"routes.AccountPasswordReset": 153, "routes.AccountLoginMFAVerifySubmit": 153,
"routes.AccountPasswordResetSubmit": 154, "routes.AccountRegisterSubmit": 154,
"routes.AccountPasswordResetToken": 155, "routes.AccountPasswordReset": 155,
"routes.AccountPasswordResetTokenSubmit": 156, "routes.AccountPasswordResetSubmit": 156,
"routes.DynamicRoute": 157, "routes.AccountPasswordResetToken": 157,
"routes.UploadedFile": 158, "routes.AccountPasswordResetTokenSubmit": 158,
"routes.StaticFile": 159, "routes.DynamicRoute": 159,
"routes.RobotsTxt": 160, "routes.UploadedFile": 160,
"routes.SitemapXml": 161, "routes.StaticFile": 161,
"routes.OpenSearchXml": 162, "routes.RobotsTxt": 162,
"routes.BadRoute": 163, "routes.SitemapXml": 163,
"routes.HTTPSRedirect": 164, "routes.OpenSearchXml": 164,
"routes.BadRoute": 165,
"routes.HTTPSRedirect": 166,
} }
var reverseRouteMapEnum = map[int]string{ var reverseRouteMapEnum = map[int]string{
0: "routes.Overview", 0: "routes.Overview",
@ -457,73 +461,75 @@ var reverseRouteMapEnum = map[int]string{
95: "routes.AccountEditAvatarSubmit", 95: "routes.AccountEditAvatarSubmit",
96: "routes.AccountEditRevokeAvatarSubmit", 96: "routes.AccountEditRevokeAvatarSubmit",
97: "routes.AccountEditUsernameSubmit", 97: "routes.AccountEditUsernameSubmit",
98: "routes.AccountEditMFA", 98: "routes.AccountEditPrivacy",
99: "routes.AccountEditMFASetup", 99: "routes.AccountEditPrivacySubmit",
100: "routes.AccountEditMFASetupSubmit", 100: "routes.AccountEditMFA",
101: "routes.AccountEditMFADisableSubmit", 101: "routes.AccountEditMFASetup",
102: "routes.AccountEditEmail", 102: "routes.AccountEditMFASetupSubmit",
103: "routes.AccountEditEmailTokenSubmit", 103: "routes.AccountEditMFADisableSubmit",
104: "routes.AccountLogins", 104: "routes.AccountEditEmail",
105: "routes.AccountBlocked", 105: "routes.AccountEditEmailTokenSubmit",
106: "routes.LevelList", 106: "routes.AccountLogins",
107: "routes.Convos", 107: "routes.AccountBlocked",
108: "routes.ConvosCreate", 108: "routes.LevelList",
109: "routes.Convo", 109: "routes.Convos",
110: "routes.ConvosCreateSubmit", 110: "routes.ConvosCreate",
111: "routes.ConvosCreateReplySubmit", 111: "routes.Convo",
112: "routes.ConvosDeleteReplySubmit", 112: "routes.ConvosCreateSubmit",
113: "routes.ConvosEditReplySubmit", 113: "routes.ConvosCreateReplySubmit",
114: "routes.RelationsBlockCreate", 114: "routes.ConvosDeleteReplySubmit",
115: "routes.RelationsBlockCreateSubmit", 115: "routes.ConvosEditReplySubmit",
116: "routes.RelationsBlockRemove", 116: "routes.RelationsBlockCreate",
117: "routes.RelationsBlockRemoveSubmit", 117: "routes.RelationsBlockCreateSubmit",
118: "routes.ViewProfile", 118: "routes.RelationsBlockRemove",
119: "routes.BanUserSubmit", 119: "routes.RelationsBlockRemoveSubmit",
120: "routes.UnbanUser", 120: "routes.ViewProfile",
121: "routes.ActivateUser", 121: "routes.BanUserSubmit",
122: "routes.IPSearch", 122: "routes.UnbanUser",
123: "routes.CreateTopicSubmit", 123: "routes.ActivateUser",
124: "routes.EditTopicSubmit", 124: "routes.IPSearch",
125: "routes.DeleteTopicSubmit", 125: "routes.CreateTopicSubmit",
126: "routes.StickTopicSubmit", 126: "routes.EditTopicSubmit",
127: "routes.UnstickTopicSubmit", 127: "routes.DeleteTopicSubmit",
128: "routes.LockTopicSubmit", 128: "routes.StickTopicSubmit",
129: "routes.UnlockTopicSubmit", 129: "routes.UnstickTopicSubmit",
130: "routes.MoveTopicSubmit", 130: "routes.LockTopicSubmit",
131: "routes.LikeTopicSubmit", 131: "routes.UnlockTopicSubmit",
132: "routes.AddAttachToTopicSubmit", 132: "routes.MoveTopicSubmit",
133: "routes.RemoveAttachFromTopicSubmit", 133: "routes.LikeTopicSubmit",
134: "routes.ViewTopic", 134: "routes.AddAttachToTopicSubmit",
135: "routes.CreateReplySubmit", 135: "routes.RemoveAttachFromTopicSubmit",
136: "routes.ReplyEditSubmit", 136: "routes.ViewTopic",
137: "routes.ReplyDeleteSubmit", 137: "routes.CreateReplySubmit",
138: "routes.ReplyLikeSubmit", 138: "routes.ReplyEditSubmit",
139: "routes.AddAttachToReplySubmit", 139: "routes.ReplyDeleteSubmit",
140: "routes.RemoveAttachFromReplySubmit", 140: "routes.ReplyLikeSubmit",
141: "routes.ProfileReplyCreateSubmit", 141: "routes.AddAttachToReplySubmit",
142: "routes.ProfileReplyEditSubmit", 142: "routes.RemoveAttachFromReplySubmit",
143: "routes.ProfileReplyDeleteSubmit", 143: "routes.ProfileReplyCreateSubmit",
144: "routes.PollVote", 144: "routes.ProfileReplyEditSubmit",
145: "routes.PollResults", 145: "routes.ProfileReplyDeleteSubmit",
146: "routes.AccountLogin", 146: "routes.PollVote",
147: "routes.AccountRegister", 147: "routes.PollResults",
148: "routes.AccountLogout", 148: "routes.AccountLogin",
149: "routes.AccountLoginSubmit", 149: "routes.AccountRegister",
150: "routes.AccountLoginMFAVerify", 150: "routes.AccountLogout",
151: "routes.AccountLoginMFAVerifySubmit", 151: "routes.AccountLoginSubmit",
152: "routes.AccountRegisterSubmit", 152: "routes.AccountLoginMFAVerify",
153: "routes.AccountPasswordReset", 153: "routes.AccountLoginMFAVerifySubmit",
154: "routes.AccountPasswordResetSubmit", 154: "routes.AccountRegisterSubmit",
155: "routes.AccountPasswordResetToken", 155: "routes.AccountPasswordReset",
156: "routes.AccountPasswordResetTokenSubmit", 156: "routes.AccountPasswordResetSubmit",
157: "routes.DynamicRoute", 157: "routes.AccountPasswordResetToken",
158: "routes.UploadedFile", 158: "routes.AccountPasswordResetTokenSubmit",
159: "routes.StaticFile", 159: "routes.DynamicRoute",
160: "routes.RobotsTxt", 160: "routes.UploadedFile",
161: "routes.SitemapXml", 161: "routes.StaticFile",
162: "routes.OpenSearchXml", 162: "routes.RobotsTxt",
163: "routes.BadRoute", 163: "routes.SitemapXml",
164: "routes.HTTPSRedirect", 164: "routes.OpenSearchXml",
165: "routes.BadRoute",
166: "routes.HTTPSRedirect",
} }
var osMapEnum = map[string]int{ var osMapEnum = map[string]int{
"unknown": 0, "unknown": 0,
@ -681,7 +687,7 @@ type HTTPSRedirect struct {}
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Connection", "close") w.Header().Set("Connection", "close")
co.RouteViewCounter.Bump(164) co.RouteViewCounter.Bump(166)
dest := "https://" + req.Host + req.URL.String() dest := "https://" + req.Host + req.URL.String()
http.Redirect(w, req, dest, http.StatusTemporaryRedirect) http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
} }
@ -889,7 +895,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
co.GlobalViewCounter.Bump() co.GlobalViewCounter.Bump()
if prefix == "/s" { //old prefix: /static if prefix == "/s" { //old prefix: /static
co.RouteViewCounter.Bump(159) co.RouteViewCounter.Bump(161)
req.URL.Path += extraData req.URL.Path += extraData
routes.StaticFile(w, req) routes.StaticFile(w, req)
return return
@ -1800,7 +1806,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
co.RouteViewCounter.Bump(97) co.RouteViewCounter.Bump(97)
err = routes.AccountEditUsernameSubmit(w,req,user) err = routes.AccountEditUsernameSubmit(w,req,user)
case "/user/edit/mfa/": case "/user/edit/privacy/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
if err != nil { if err != nil {
return err return err
@ -1808,6 +1814,31 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
co.RouteViewCounter.Bump(98) co.RouteViewCounter.Bump(98)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
}
err = routes.AccountEditPrivacy(w,req,user,head)
case "/user/edit/privacy/submit/":
err = c.NoSessionMismatch(w,req,user)
if err != nil {
return err
}
err = c.MemberOnly(w,req,user)
if err != nil {
return err
}
co.RouteViewCounter.Bump(99)
err = routes.AccountEditPrivacySubmit(w,req,user)
case "/user/edit/mfa/":
err = c.MemberOnly(w,req,user)
if err != nil {
return err
}
co.RouteViewCounter.Bump(100)
head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
} }
@ -1818,7 +1849,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(99) co.RouteViewCounter.Bump(101)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1835,7 +1866,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(100) co.RouteViewCounter.Bump(102)
err = routes.AccountEditMFASetupSubmit(w,req,user) err = routes.AccountEditMFASetupSubmit(w,req,user)
case "/user/edit/mfa/disable/submit/": case "/user/edit/mfa/disable/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -1848,7 +1879,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(101) co.RouteViewCounter.Bump(103)
err = routes.AccountEditMFADisableSubmit(w,req,user) err = routes.AccountEditMFADisableSubmit(w,req,user)
case "/user/edit/email/": case "/user/edit/email/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -1856,14 +1887,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(102) co.RouteViewCounter.Bump(104)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
} }
err = routes.AccountEditEmail(w,req,user,head) err = routes.AccountEditEmail(w,req,user,head)
case "/user/edit/token/": case "/user/edit/token/":
co.RouteViewCounter.Bump(103) co.RouteViewCounter.Bump(105)
err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData) err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData)
case "/user/edit/logins/": case "/user/edit/logins/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -1871,7 +1902,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(104) co.RouteViewCounter.Bump(106)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1883,7 +1914,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(105) co.RouteViewCounter.Bump(107)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1895,7 +1926,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(106) co.RouteViewCounter.Bump(108)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1907,7 +1938,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(107) co.RouteViewCounter.Bump(109)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1919,7 +1950,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(108) co.RouteViewCounter.Bump(110)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1931,7 +1962,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(109) co.RouteViewCounter.Bump(111)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -1948,7 +1979,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(110) co.RouteViewCounter.Bump(112)
err = routes.ConvosCreateSubmit(w,req,user) err = routes.ConvosCreateSubmit(w,req,user)
case "/user/convo/create/submit/": case "/user/convo/create/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -1961,7 +1992,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(111) co.RouteViewCounter.Bump(113)
err = routes.ConvosCreateReplySubmit(w,req,user,extraData) err = routes.ConvosCreateReplySubmit(w,req,user,extraData)
case "/user/convo/delete/submit/": case "/user/convo/delete/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -1974,7 +2005,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(112) co.RouteViewCounter.Bump(114)
err = routes.ConvosDeleteReplySubmit(w,req,user,extraData) err = routes.ConvosDeleteReplySubmit(w,req,user,extraData)
case "/user/convo/edit/submit/": case "/user/convo/edit/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -1987,7 +2018,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(113) co.RouteViewCounter.Bump(115)
err = routes.ConvosEditReplySubmit(w,req,user,extraData) err = routes.ConvosEditReplySubmit(w,req,user,extraData)
case "/user/block/create/": case "/user/block/create/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -1995,7 +2026,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(114) co.RouteViewCounter.Bump(116)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2012,7 +2043,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(115) co.RouteViewCounter.Bump(117)
err = routes.RelationsBlockCreateSubmit(w,req,user,extraData) err = routes.RelationsBlockCreateSubmit(w,req,user,extraData)
case "/user/block/remove/": case "/user/block/remove/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -2020,7 +2051,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(116) co.RouteViewCounter.Bump(118)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2037,11 +2068,11 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(117) co.RouteViewCounter.Bump(119)
err = routes.RelationsBlockRemoveSubmit(w,req,user,extraData) err = routes.RelationsBlockRemoveSubmit(w,req,user,extraData)
default: default:
req.URL.Path += extraData req.URL.Path += extraData
co.RouteViewCounter.Bump(118) co.RouteViewCounter.Bump(120)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2061,7 +2092,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(119) co.RouteViewCounter.Bump(121)
err = routes.BanUserSubmit(w,req,user,extraData) err = routes.BanUserSubmit(w,req,user,extraData)
case "/users/unban/": case "/users/unban/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2074,7 +2105,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(120) co.RouteViewCounter.Bump(122)
err = routes.UnbanUser(w,req,user,extraData) err = routes.UnbanUser(w,req,user,extraData)
case "/users/activate/": case "/users/activate/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2087,7 +2118,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(121) co.RouteViewCounter.Bump(123)
err = routes.ActivateUser(w,req,user,extraData) err = routes.ActivateUser(w,req,user,extraData)
case "/users/ips/": case "/users/ips/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -2095,7 +2126,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(122) co.RouteViewCounter.Bump(124)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2119,7 +2150,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(123) co.RouteViewCounter.Bump(125)
err = routes.CreateTopicSubmit(w,req,user) err = routes.CreateTopicSubmit(w,req,user)
case "/topic/edit/submit/": case "/topic/edit/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2132,7 +2163,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(124) co.RouteViewCounter.Bump(126)
err = routes.EditTopicSubmit(w,req,user,extraData) err = routes.EditTopicSubmit(w,req,user,extraData)
case "/topic/delete/submit/": case "/topic/delete/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2146,7 +2177,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
} }
req.URL.Path += extraData req.URL.Path += extraData
co.RouteViewCounter.Bump(125) co.RouteViewCounter.Bump(127)
err = routes.DeleteTopicSubmit(w,req,user) err = routes.DeleteTopicSubmit(w,req,user)
case "/topic/stick/submit/": case "/topic/stick/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2159,7 +2190,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(126) co.RouteViewCounter.Bump(128)
err = routes.StickTopicSubmit(w,req,user,extraData) err = routes.StickTopicSubmit(w,req,user,extraData)
case "/topic/unstick/submit/": case "/topic/unstick/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2172,7 +2203,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(127) co.RouteViewCounter.Bump(129)
err = routes.UnstickTopicSubmit(w,req,user,extraData) err = routes.UnstickTopicSubmit(w,req,user,extraData)
case "/topic/lock/submit/": case "/topic/lock/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2186,7 +2217,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
} }
req.URL.Path += extraData req.URL.Path += extraData
co.RouteViewCounter.Bump(128) co.RouteViewCounter.Bump(130)
err = routes.LockTopicSubmit(w,req,user) err = routes.LockTopicSubmit(w,req,user)
case "/topic/unlock/submit/": case "/topic/unlock/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2199,7 +2230,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(129) co.RouteViewCounter.Bump(131)
err = routes.UnlockTopicSubmit(w,req,user,extraData) err = routes.UnlockTopicSubmit(w,req,user,extraData)
case "/topic/move/submit/": case "/topic/move/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2212,7 +2243,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(130) co.RouteViewCounter.Bump(132)
err = routes.MoveTopicSubmit(w,req,user,extraData) err = routes.MoveTopicSubmit(w,req,user,extraData)
case "/topic/like/submit/": case "/topic/like/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2225,7 +2256,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(131) co.RouteViewCounter.Bump(133)
err = routes.LikeTopicSubmit(w,req,user,extraData) err = routes.LikeTopicSubmit(w,req,user,extraData)
case "/topic/attach/add/submit/": case "/topic/attach/add/submit/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -2242,7 +2273,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(132) co.RouteViewCounter.Bump(134)
err = routes.AddAttachToTopicSubmit(w,req,user,extraData) err = routes.AddAttachToTopicSubmit(w,req,user,extraData)
case "/topic/attach/remove/submit/": case "/topic/attach/remove/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2255,10 +2286,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(133) co.RouteViewCounter.Bump(135)
err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData)
default: default:
co.RouteViewCounter.Bump(134) co.RouteViewCounter.Bump(136)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2282,7 +2313,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(135) co.RouteViewCounter.Bump(137)
err = routes.CreateReplySubmit(w,req,user) err = routes.CreateReplySubmit(w,req,user)
case "/reply/edit/submit/": case "/reply/edit/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2295,7 +2326,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(136) co.RouteViewCounter.Bump(138)
err = routes.ReplyEditSubmit(w,req,user,extraData) err = routes.ReplyEditSubmit(w,req,user,extraData)
case "/reply/delete/submit/": case "/reply/delete/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2308,7 +2339,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(137) co.RouteViewCounter.Bump(139)
err = routes.ReplyDeleteSubmit(w,req,user,extraData) err = routes.ReplyDeleteSubmit(w,req,user,extraData)
case "/reply/like/submit/": case "/reply/like/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2321,7 +2352,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(138) co.RouteViewCounter.Bump(140)
err = routes.ReplyLikeSubmit(w,req,user,extraData) err = routes.ReplyLikeSubmit(w,req,user,extraData)
case "/reply/attach/add/submit/": case "/reply/attach/add/submit/":
err = c.MemberOnly(w,req,user) err = c.MemberOnly(w,req,user)
@ -2338,7 +2369,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(139) co.RouteViewCounter.Bump(141)
err = routes.AddAttachToReplySubmit(w,req,user,extraData) err = routes.AddAttachToReplySubmit(w,req,user,extraData)
case "/reply/attach/remove/submit/": case "/reply/attach/remove/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2351,7 +2382,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(140) co.RouteViewCounter.Bump(142)
err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData)
} }
case "/profile": case "/profile":
@ -2367,7 +2398,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(141) co.RouteViewCounter.Bump(143)
err = routes.ProfileReplyCreateSubmit(w,req,user) err = routes.ProfileReplyCreateSubmit(w,req,user)
case "/profile/reply/edit/submit/": case "/profile/reply/edit/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2380,7 +2411,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(142) co.RouteViewCounter.Bump(144)
err = routes.ProfileReplyEditSubmit(w,req,user,extraData) err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
case "/profile/reply/delete/submit/": case "/profile/reply/delete/submit/":
err = c.NoSessionMismatch(w,req,user) err = c.NoSessionMismatch(w,req,user)
@ -2393,7 +2424,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(143) co.RouteViewCounter.Bump(145)
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
} }
case "/poll": case "/poll":
@ -2409,23 +2440,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(144) co.RouteViewCounter.Bump(146)
err = routes.PollVote(w,req,user,extraData) err = routes.PollVote(w,req,user,extraData)
case "/poll/results/": case "/poll/results/":
co.RouteViewCounter.Bump(145) co.RouteViewCounter.Bump(147)
err = routes.PollResults(w,req,user,extraData) err = routes.PollResults(w,req,user,extraData)
} }
case "/accounts": case "/accounts":
switch(req.URL.Path) { switch(req.URL.Path) {
case "/accounts/login/": case "/accounts/login/":
co.RouteViewCounter.Bump(146) co.RouteViewCounter.Bump(148)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
} }
err = routes.AccountLogin(w,req,user,head) err = routes.AccountLogin(w,req,user,head)
case "/accounts/create/": case "/accounts/create/":
co.RouteViewCounter.Bump(147) co.RouteViewCounter.Bump(149)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2442,7 +2473,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(148) co.RouteViewCounter.Bump(150)
err = routes.AccountLogout(w,req,user) err = routes.AccountLogout(w,req,user)
case "/accounts/login/submit/": case "/accounts/login/submit/":
err = c.ParseForm(w,req,user) err = c.ParseForm(w,req,user)
@ -2450,10 +2481,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(149) co.RouteViewCounter.Bump(151)
err = routes.AccountLoginSubmit(w,req,user) err = routes.AccountLoginSubmit(w,req,user)
case "/accounts/mfa_verify/": case "/accounts/mfa_verify/":
co.RouteViewCounter.Bump(150) co.RouteViewCounter.Bump(152)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2465,7 +2496,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(151) co.RouteViewCounter.Bump(153)
err = routes.AccountLoginMFAVerifySubmit(w,req,user) err = routes.AccountLoginMFAVerifySubmit(w,req,user)
case "/accounts/create/submit/": case "/accounts/create/submit/":
err = c.ParseForm(w,req,user) err = c.ParseForm(w,req,user)
@ -2473,10 +2504,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(152) co.RouteViewCounter.Bump(154)
err = routes.AccountRegisterSubmit(w,req,user) err = routes.AccountRegisterSubmit(w,req,user)
case "/accounts/password-reset/": case "/accounts/password-reset/":
co.RouteViewCounter.Bump(153) co.RouteViewCounter.Bump(155)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2488,10 +2519,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(154) co.RouteViewCounter.Bump(156)
err = routes.AccountPasswordResetSubmit(w,req,user) err = routes.AccountPasswordResetSubmit(w,req,user)
case "/accounts/password-reset/token/": case "/accounts/password-reset/token/":
co.RouteViewCounter.Bump(155) co.RouteViewCounter.Bump(157)
head, err := c.UserCheck(w,req,&user) head, err := c.UserCheck(w,req,&user)
if err != nil { if err != nil {
return err return err
@ -2503,7 +2534,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err return err
} }
co.RouteViewCounter.Bump(156) co.RouteViewCounter.Bump(158)
err = routes.AccountPasswordResetTokenSubmit(w,req,user) err = routes.AccountPasswordResetTokenSubmit(w,req,user)
} }
/*case "/sitemaps": // TODO: Count these views /*case "/sitemaps": // TODO: Count these views
@ -2520,7 +2551,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
h.Del("Content-Type") h.Del("Content-Type")
h.Del("Content-Encoding") h.Del("Content-Encoding")
} }
co.RouteViewCounter.Bump(158) co.RouteViewCounter.Bump(160)
req.URL.Path += extraData req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this? // TODO: Find a way to propagate errors up from this?
r.UploadHandler(w,req) // TODO: Count these views r.UploadHandler(w,req) // TODO: Count these views
@ -2530,7 +2561,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
// TODO: Add support for favicons and robots.txt files // TODO: Add support for favicons and robots.txt files
switch(extraData) { switch(extraData) {
case "robots.txt": case "robots.txt":
co.RouteViewCounter.Bump(160) co.RouteViewCounter.Bump(162)
return routes.RobotsTxt(w,req) return routes.RobotsTxt(w,req)
case "favicon.ico": case "favicon.ico":
gzw, ok := w.(c.GzipResponseWriter) gzw, ok := w.(c.GzipResponseWriter)
@ -2544,10 +2575,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
routes.StaticFile(w,req) routes.StaticFile(w,req)
return nil return nil
case "opensearch.xml": case "opensearch.xml":
co.RouteViewCounter.Bump(162) co.RouteViewCounter.Bump(164)
return routes.OpenSearchXml(w,req) return routes.OpenSearchXml(w,req)
/*case "sitemap.xml": /*case "sitemap.xml":
co.RouteViewCounter.Bump(161) co.RouteViewCounter.Bump(163)
return routes.SitemapXml(w,req)*/ return routes.SitemapXml(w,req)*/
} }
return c.NotFound(w,req,nil) return c.NotFound(w,req,nil)
@ -2558,7 +2589,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
r.RUnlock() r.RUnlock()
if ok { if ok {
co.RouteViewCounter.Bump(157) // TODO: Be more specific about *which* dynamic route it is co.RouteViewCounter.Bump(159) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData req.URL.Path += extraData
return handle(w,req,user) return handle(w,req,user)
} }
@ -2569,7 +2600,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
} else { } else {
r.DumpRequest(req,"Bad Route") r.DumpRequest(req,"Bad Route")
} }
co.RouteViewCounter.Bump(163) co.RouteViewCounter.Bump(165)
return c.NotFound(w,req,nil) return c.NotFound(w,req,nil)
} }
return err return err

View File

@ -791,7 +791,7 @@ func BenchmarkQueryTopicParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
var tu c.TopicUser var tu c.TopicUser
for pb.Next() { for pb.Next() {
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.views, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.views, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
if err == ErrNoRows { if err == ErrNoRows {
log.Fatal("No rows found!") log.Fatal("No rows found!")
return return
@ -812,14 +812,14 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
var tu c.TopicUser var tu c.TopicUser
getTopicUser, err := qgen.Builder.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", "") getTopicUser, err := qgen.Builder.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level", "topics.createdBy = users.uid", "tid = ?", "", "")
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
defer getTopicUser.Close() defer getTopicUser.Close()
for pb.Next() { for pb.Next() {
err := getTopicUser.QueryRow(1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) err := getTopicUser.QueryRow(1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
if err == ErrNoRows { if err == ErrNoRows {
b.Fatal("No rows found!") b.Fatal("No rows found!")
return return
@ -873,7 +873,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
var tu c.TopicUser var tu c.TopicUser
b.Run("topic", func(b *testing.B) { b.Run("topic", func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
if err == ErrNoRows { if err == ErrNoRows {
b.Fatal("No rows found!") b.Fatal("No rows found!")
return return
@ -885,7 +885,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
}) })
b.Run("topic_replies", func(b *testing.B) { b.Run("topic_replies", func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1) rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
return return
@ -907,13 +907,13 @@ func BenchmarkQueriesSerial(b *testing.B) {
var group int var group int
b.Run("topic_replies_scan", func(b *testing.B) { b.Run("topic_replies_scan", func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1) rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
return return
} }
for rows.Next() { for rows.Next() {
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &isSuperAdmin, &group, &r.URLPrefix, &r.URLName, &r.Level, &r.IP) err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &isSuperAdmin, &group, &r.Level, &r.IP)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
return return
@ -933,36 +933,19 @@ func BenchmarkQueriesSerial(b *testing.B) {
// TODO: Take the attachment system into account in these parser benches // TODO: Take the attachment system into account in these parser benches
func BenchmarkParserSerial(b *testing.B) { func BenchmarkParserSerial(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.Run("empty_post", func(b *testing.B) { f := func(name, msg string) func(b *testing.B) {
return func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_ = c.ParseMessage("", 0, "") _ = c.ParseMessage(msg, 0, "", nil)
} }
})
b.Run("short_post", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = c.ParseMessage("Hey everyone, how's it going?", 0, "")
} }
})
b.Run("one_smily", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = c.ParseMessage("Hey everyone, how's it going? :)", 0, "")
} }
}) f("empty_post","")
b.Run("five_smilies", func(b *testing.B) { f("short_post","Hey everyone, how's it going?")
for i := 0; i < b.N; i++ { f("one_smily","Hey everyone, how's it going? :)")
_ = c.ParseMessage("Hey everyone, how's it going? :):):):):)", 0, "") f("five_smilies","Hey everyone, how's it going? :):):):):)")
} f("ten_smilies","Hey everyone, how's it going? :):):):):):):):):):)")
}) f("twenty_smilies","Hey everyone, how's it going? :):):):):):):):):):):):):):):):):):):):)")
b.Run("ten_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = c.ParseMessage("Hey everyone, how's it going? :):):):):):):):):):)", 0, "")
}
})
b.Run("twenty_smilies", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = c.ParseMessage("Hey everyone, how's it going? :):):):):):):):):):):):):):):):):):):):)", 0, "")
}
})
} }
func BenchmarkBBCodePluginWithRegexpSerial(b *testing.B) { func BenchmarkBBCodePluginWithRegexpSerial(b *testing.B) {

View File

@ -293,7 +293,7 @@ func TestParser(t *testing.T) {
// TODO: Fix this hack and make the results a bit more reproducible, push the tests further in the process. // TODO: Fix this hack and make the results a bit more reproducible, push the tests further in the process.
for _, item := range l.Items { for _, item := range l.Items {
if res := c.ParseMessage(item.Msg, 1, "forums"); res != item.Expects { if res := c.ParseMessage(item.Msg, 1, "forums", nil); res != item.Expects {
if item.Name != "" { if item.Name != "" {
t.Error("Name: ", item.Name) t.Error("Name: ", item.Name)
} }
@ -314,7 +314,7 @@ func TestParser(t *testing.T) {
l.Add("//"+c.Site.URL+"\n", "<a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a><br>") l.Add("//"+c.Site.URL+"\n", "<a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a><br>")
l.Add("//"+c.Site.URL+"\n//"+c.Site.URL, "<a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a><br><a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a>") l.Add("//"+c.Site.URL+"\n//"+c.Site.URL, "<a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a><br><a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a>")
for _, item := range l.Items { for _, item := range l.Items {
if res := c.ParseMessage(item.Msg, 1, "forums"); res != item.Expects { if res := c.ParseMessage(item.Msg, 1, "forums", nil); res != item.Expects {
if item.Name != "" { if item.Name != "" {
t.Error("Name: ", item.Name) t.Error("Name: ", item.Name)
} }
@ -338,7 +338,7 @@ func TestParser(t *testing.T) {
} }
c.WriteURL(sb, c.BuildTopicURL("", tid), "#nnid-"+strconv.Itoa(tid)) c.WriteURL(sb, c.BuildTopicURL("", tid), "#nnid-"+strconv.Itoa(tid))
}) })
res := c.ParseMessage("#nnid-1", 1, "forums") res := c.ParseMessage("#nnid-1", 1, "forums", nil)
expect := "<a href='/topic/1'>#nnid-1</a>" expect := "<a href='/topic/1'>#nnid-1</a>"
if res != expect { if res != expect {
t.Error("Bad output:", "'"+res+"'") t.Error("Bad output:", "'"+res+"'")
@ -356,7 +356,7 @@ func TestParser(t *testing.T) {
} }
c.WriteURL(sb, c.BuildTopicURL("", tid), "#longidnameneedtooverflowhack-"+strconv.Itoa(tid)) c.WriteURL(sb, c.BuildTopicURL("", tid), "#longidnameneedtooverflowhack-"+strconv.Itoa(tid))
}) })
res = c.ParseMessage("#longidnameneedtooverflowhack-1", 1, "forums") res = c.ParseMessage("#longidnameneedtooverflowhack-1", 1, "forums", nil)
expect = "<a href='/topic/1'>#longidnameneedtooverflowhack-1</a>" expect = "<a href='/topic/1'>#longidnameneedtooverflowhack-1</a>"
if res != expect { if res != expect {
t.Error("Bad output:", "'"+res+"'") t.Error("Bad output:", "'"+res+"'")

View File

@ -42,6 +42,7 @@ func init() {
addPatch(25, patch25) addPatch(25, patch25)
addPatch(26, patch26) addPatch(26, patch26)
addPatch(27, patch27) addPatch(27, patch27)
addPatch(28, patch28)
} }
func patch0(scanner *bufio.Scanner) (err error) { func patch0(scanner *bufio.Scanner) (err error) {
@ -745,3 +746,7 @@ func patch27(scanner *bufio.Scanner) error {
} }
return execStmt(qgen.Builder.AddColumn("administration_logs", tC{"extra", "text", 0, false, false, ""}, nil)) return execStmt(qgen.Builder.AddColumn("administration_logs", tC{"extra", "text", 0, false, false, ""}, nil))
} }
func patch28(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("users", tC{"enable_embeds", "int", 0, false, false, "-1"}, nil))
}

View File

@ -56,6 +56,8 @@ func userRoutes() *RouteGroup {
UploadAction("AvatarSubmit", "/avatar/submit/").MaxSizeVar("int(c.Config.MaxRequestSize)"), UploadAction("AvatarSubmit", "/avatar/submit/").MaxSizeVar("int(c.Config.MaxRequestSize)"),
Action("RevokeAvatarSubmit", "/avatar/revoke/submit/"), Action("RevokeAvatarSubmit", "/avatar/revoke/submit/"),
Action("UsernameSubmit", "/username/submit/"), // TODO: Full test this Action("UsernameSubmit", "/username/submit/"), // TODO: Full test this
MView("Privacy", "/privacy/"),
Action("PrivacySubmit", "/privacy/submit/"),
MView("MFA", "/mfa/"), MView("MFA", "/mfa/"),
MView("MFASetup", "/mfa/setup/"), MView("MFASetup", "/mfa/setup/"),
Action("MFASetupSubmit", "/mfa/setup/submit/"), Action("MFASetupSubmit", "/mfa/setup/submit/"),

View File

@ -14,7 +14,7 @@ import (
c "github.com/Azareal/Gosora/common" c "github.com/Azareal/Gosora/common"
p "github.com/Azareal/Gosora/common/phrases" p "github.com/Azareal/Gosora/common/phrases"
"github.com/Azareal/Gosora/query_gen" qgen "github.com/Azareal/Gosora/query_gen"
) )
// A blank list to fill out that parameter in Page for routes which don't use it // A blank list to fill out that parameter in Page for routes which don't use it
@ -235,7 +235,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
if isNumeric(nameBits[0]) { if isNumeric(nameBits[0]) {
regError(p.GetErrorPhrase("register_first_word_numeric"), "numeric-name") regError(p.GetErrorPhrase("register_first_word_numeric"), "numeric-name")
} }
if strings.Contains(name,"http://") || strings.Contains(name,"https://") || strings.Contains(name,"ftp://") || strings.Contains(name,"ssh://") { if strings.Contains(name, "http://") || strings.Contains(name, "https://") || strings.Contains(name, "ftp://") || strings.Contains(name, "ssh://") {
regError(p.GetErrorPhrase("register_url_username"), "url-name") regError(p.GetErrorPhrase("register_url_username"), "url-name")
} }
@ -423,11 +423,11 @@ func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c.User
return c.NoPermissions(w, r, user) return c.NoPermissions(w, r, user)
} }
ext, ferr := c.UploadAvatar(w,r,user,user.ID) ext, ferr := c.UploadAvatar(w, r, user, user.ID)
if ferr != nil { if ferr != nil {
return ferr return ferr
} }
ferr = c.ChangeAvatar("." + ext, w, r, user) ferr = c.ChangeAvatar("."+ext, w, r, user)
if ferr != nil { if ferr != nil {
return ferr return ferr
} }
@ -572,6 +572,37 @@ func AccountEditMFADisableSubmit(w http.ResponseWriter, r *http.Request, user c.
return nil return nil
} }
func AccountEditPrivacy(w http.ResponseWriter, r *http.Request, user c.User, h *c.Header) c.RouteError {
accountEditHead("account_privacy", w, r, &user, h)
profileComments := false
receiveConvos := false
enableEmbeds := !c.DefaultParseSettings.NoEmbed
if user.ParseSettings != nil {
enableEmbeds = !user.ParseSettings.NoEmbed
}
pi := c.Account{h, "privacy", "account_own_edit_privacy", c.AccountPrivacyPage{h, profileComments, receiveConvos, enableEmbeds}}
return renderTemplate("account", w, r, h, pi)
}
func AccountEditPrivacySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
//headerLite, _ := c.SimpleUserCheck(w, r, &user)
sEnableEmbeds := r.FormValue("enable_embeds")
enableEmbeds, err := strconv.Atoi(sEnableEmbeds)
if err != nil {
return c.LocalError("enable_embeds must be 0 or 1", w, r, user)
}
if sEnableEmbeds != r.FormValue("o_enable_embeds") {
err = (&user).UpdatePrivacy(enableEmbeds)
if err != nil {
return c.InternalError(err, w, r)
}
}
http.Redirect(w, r, "/user/edit/privacy/?updated=1", http.StatusSeeOther)
return nil
}
func AccountEditEmail(w http.ResponseWriter, r *http.Request, user c.User, h *c.Header) c.RouteError { func AccountEditEmail(w http.ResponseWriter, r *http.Request, user c.User, h *c.Header) c.RouteError {
accountEditHead("account_email", w, r, &user, h) accountEditHead("account_email", w, r, &user, h)
emails, err := c.Emails.GetEmailsByUser(&user) emails, err := c.Emails.GetEmailsByUser(&user)
@ -598,10 +629,9 @@ func AccountEditEmail(w http.ResponseWriter, r *http.Request, user c.User, h *c.
func AccountEditEmailAddSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { func AccountEditEmailAddSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
email := r.PostFormValue("email") email := r.PostFormValue("email")
_, err := c.Emails.Get(&user, email) _, err := c.Emails.Get(&user, email)
if err == nil { if err == nil {
return c.LocalError("You have already added this email.",w,r,user) return c.LocalError("You have already added this email.", w, r, user)
} else if err != sql.ErrNoRows && err != nil { } else if err != sql.ErrNoRows && err != nil {
return c.InternalError(err, w, r) return c.InternalError(err, w, r)
} }
@ -615,7 +645,7 @@ func AccountEditEmailAddSubmit(w http.ResponseWriter, r *http.Request, user c.Us
} }
err = c.Emails.Add(user.ID, email, token) err = c.Emails.Add(user.ID, email, token)
if err != nil { if err != nil {
return c.InternalError(err,w,r) return c.InternalError(err, w, r)
} }
if c.Site.EnableEmails { if c.Site.EnableEmails {
err = c.SendValidationEmail(user.Name, email, token) err = c.SendValidationEmail(user.Name, email, token)
@ -635,17 +665,17 @@ func AccountEditEmailRemoveSubmit(w http.ResponseWriter, r *http.Request, user c
// Quick and dirty check // Quick and dirty check
_, err := c.Emails.Get(&user, email) _, err := c.Emails.Get(&user, email)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return c.LocalError("This email isn't set on this user.",w,r,user) return c.LocalError("This email isn't set on this user.", w, r, user)
} else if err != nil { } else if err != nil {
return c.InternalError(err, w, r) return c.InternalError(err, w, r)
} }
if headerLite.Settings["activation_type"] == 2 && user.Email == email { if headerLite.Settings["activation_type"] == 2 && user.Email == email {
return c.LocalError("You can't remove your primary email when mandatory email activation is enabled.",w,r,user) return c.LocalError("You can't remove your primary email when mandatory email activation is enabled.", w, r, user)
} }
err = c.Emails.Delete(user.ID, email) err = c.Emails.Delete(user.ID, email)
if err != nil { if err != nil {
return c.InternalError(err,w,r) return c.InternalError(err, w, r)
} }
http.Redirect(w, r, "/user/edit/email/?removed=1", http.StatusSeeOther) http.Redirect(w, r, "/user/edit/email/?removed=1", http.StatusSeeOther)
@ -729,13 +759,13 @@ func AccountBlocked(w http.ResponseWriter, r *http.Request, user c.User, h *c.He
for _, uid := range uids { for _, uid := range uids {
u, err := c.Users.Get(uid) u, err := c.Users.Get(uid)
if err != nil { if err != nil {
return c.InternalError(err,w,r) return c.InternalError(err, w, r)
} }
blocks = append(blocks, u) blocks = append(blocks, u)
} }
pageList := c.Paginate(page, lastPage, 5) pageList := c.Paginate(page, lastPage, 5)
pi := c.Account{h, "logins", "account_blocked", c.AccountBlocksPage{h, blocks, c.Paginator{pageList, page, lastPage}}} pi := c.Account{h, "blocked", "account_blocked", c.AccountBlocksPage{h, blocks, c.Paginator{pageList, page, lastPage}}}
return renderTemplate("account", w, r, h, pi) return renderTemplate("account", w, r, h, pi)
} }

View File

@ -78,7 +78,7 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user c.User, header *c.
replyLiked := false replyLiked := false
replyLikeCount := 0 replyLikeCount := 0
ru := &c.ReplyUser{Reply: c.Reply{rid, puser.ID, replyContent, replyCreatedBy, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, 0, "", replyLiked, replyLikeCount, 0, ""}, ContentHtml: c.ParseMessage(replyContent, 0, ""), CreatedByName: replyCreatedByName, Avatar: replyAvatar, Level: 0} ru := &c.ReplyUser{Reply: c.Reply{rid, puser.ID, replyContent, replyCreatedBy, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, 0, "", replyLiked, replyLikeCount, 0, ""}, ContentHtml: c.ParseMessage(replyContent, 0, "", user.ParseSettings), CreatedByName: replyCreatedByName, Avatar: replyAvatar, Level: 0}
ru.Init() ru.Init()
group, err := c.Groups.Get(ru.Group) group, err := c.Groups.Get(ru.Group)

View File

@ -194,7 +194,7 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
prid, _ := strconv.Atoi(r.FormValue("prid")) prid, _ := strconv.Atoi(r.FormValue("prid"))
if js && (prid == 0 || rids[0] == prid) { if js && (prid == 0 || rids[0] == prid) {
outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums")}) outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums", user.ParseSettings)})
if err != nil { if err != nil {
return c.InternalErrorJSQ(err, w, r, js) return c.InternalErrorJSQ(err, w, r, js)
} }
@ -267,7 +267,7 @@ func ReplyEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, srid s
if !js { if !js {
http.Redirect(w, r, "/topic/"+strconv.Itoa(topic.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther) http.Redirect(w, r, "/topic/"+strconv.Itoa(topic.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else { } else {
outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums")}) outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums", user.ParseSettings)})
if err != nil { if err != nil {
return c.InternalErrorJSQ(err, w, r, js) return c.InternalErrorJSQ(err, w, r, js)
} }

View File

@ -9,17 +9,17 @@ import (
"io" "io"
//"fmt" //"fmt"
"golang.org/x/image/tiff"
"image"
"image/gif"
"image/jpeg"
"image/png"
"log" "log"
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"image"
"image/gif"
"image/jpeg"
"image/png"
"golang.org/x/image/tiff"
c "github.com/Azareal/Gosora/common" c "github.com/Azareal/Gosora/common"
"github.com/Azareal/Gosora/common/counters" "github.com/Azareal/Gosora/common/counters"
@ -72,16 +72,17 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He
header.Path = c.BuildTopicURL(c.NameToSlug(topic.Title), topic.ID) header.Path = c.BuildTopicURL(c.NameToSlug(topic.Title), topic.ID)
// TODO: Cache ContentHTML when possible? // TODO: Cache ContentHTML when possible?
topic.ContentHTML = c.ParseMessage(topic.Content, topic.ParentID, "forums") topic.ContentHTML = c.ParseMessage(topic.Content, topic.ParentID, "forums", user.ParseSettings)
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do. // TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
if topic.ContentHTML == topic.Content { if topic.ContentHTML == topic.Content {
topic.ContentHTML = topic.Content topic.ContentHTML = topic.Content
} }
topic.ContentLines = strings.Count(topic.Content, "\n") topic.ContentLines = strings.Count(topic.Content, "\n")
if len(topic.Content) > 200 {
header.OGDesc = topic.Content[:197] + "..."
} else {
header.OGDesc = topic.Content header.OGDesc = topic.Content
if len(header.OGDesc) > 200 {
header.OGDesc = header.OGDesc[:197] + "..."
} }
postGroup, err := c.Groups.Get(topic.Group) postGroup, err := c.Groups.Get(topic.Group)
@ -141,7 +142,6 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He
if strings.HasPrefix(r.URL.Fragment, "post-") { if strings.HasPrefix(r.URL.Fragment, "post-") {
pFrag, _ = strconv.Atoi(strings.TrimPrefix(r.URL.Fragment, "post-")) pFrag, _ = strconv.Atoi(strings.TrimPrefix(r.URL.Fragment, "post-"))
} }
rlist, ogdesc, err := topic.Replies(offset, pFrag, &user) rlist, ogdesc, err := topic.Replies(offset, pFrag, &user)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return c.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user) return c.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user)
@ -510,7 +510,7 @@ func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, user c.User, di
} else { } else {
img, _, err := image.Decode(inFile) img, _, err := image.Decode(inFile)
if err != nil { if err != nil {
return nil, c.LocalError("Upload failed [Image Decoding Failed]",w,r,user) return nil, c.LocalError("Upload failed [Image Decoding Failed]", w, r, user)
} }
outFile, err := os.Create(dir + filename) outFile, err := os.Create(dir + filename)
@ -524,13 +524,13 @@ func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, user c.User, di
err = gif.Encode(outFile, img, nil) err = gif.Encode(outFile, img, nil)
case "png": case "png":
err = png.Encode(outFile, img) err = png.Encode(outFile, img)
case "tiff","tif": case "tiff", "tif":
err = tiff.Encode(outFile,img,nil) err = tiff.Encode(outFile, img, nil)
default: default:
err = jpeg.Encode(outFile, img, nil) err = jpeg.Encode(outFile, img, nil)
} }
if err != nil { if err != nil {
return nil, c.LocalError("Upload failed [Image Encoding Failed]", w,r,user) return nil, c.LocalError("Upload failed [Image Encoding Failed]", w, r, user)
} }
} }
@ -603,7 +603,7 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s
if !js { if !js {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
} else { } else {
outBytes, err := json.Marshal(JsonReply{c.ParseMessage(topic.Content, topic.ParentID, "forums")}) outBytes, err := json.Marshal(JsonReply{c.ParseMessage(topic.Content, topic.ParentID, "forums", user.ParseSettings)})
if err != nil { if err != nil {
return c.InternalErrorJSQ(err, w, r, js) return c.InternalErrorJSQ(err, w, r, js)
} }

View File

@ -10,6 +10,7 @@ CREATE TABLE [users] (
[lastActiveAt] datetime not null, [lastActiveAt] datetime not null,
[session] nvarchar (200) DEFAULT '' not null, [session] nvarchar (200) DEFAULT '' not null,
[last_ip] nvarchar (200) DEFAULT '0.0.0.0.0' not null, [last_ip] nvarchar (200) DEFAULT '0.0.0.0.0' not null,
[enable_embeds] int DEFAULT -1 not null,
[email] nvarchar (200) DEFAULT '' not null, [email] nvarchar (200) DEFAULT '' not null,
[avatar] nvarchar (100) DEFAULT '' not null, [avatar] nvarchar (100) DEFAULT '' not null,
[message] nvarchar (MAX) DEFAULT '' not null, [message] nvarchar (MAX) DEFAULT '' not null,

View File

@ -10,6 +10,7 @@ CREATE TABLE `users` (
`lastActiveAt` datetime not null, `lastActiveAt` datetime not null,
`session` varchar(200) DEFAULT '' not null, `session` varchar(200) DEFAULT '' not null,
`last_ip` varchar(200) DEFAULT '0.0.0.0.0' not null, `last_ip` varchar(200) DEFAULT '0.0.0.0.0' not null,
`enable_embeds` int DEFAULT -1 not null,
`email` varchar(200) DEFAULT '' not null, `email` varchar(200) DEFAULT '' not null,
`avatar` varchar(100) DEFAULT '' not null, `avatar` varchar(100) DEFAULT '' not null,
`message` text not null, `message` text not null,

View File

@ -10,6 +10,7 @@ CREATE TABLE "users" (
`lastActiveAt` timestamp not null, `lastActiveAt` timestamp not null,
`session` varchar (200) DEFAULT '' not null, `session` varchar (200) DEFAULT '' not null,
`last_ip` varchar (200) DEFAULT '0.0.0.0.0' not null, `last_ip` varchar (200) DEFAULT '0.0.0.0.0' not null,
`enable_embeds` int DEFAULT -1 not null,
`email` varchar (200) DEFAULT '' not null, `email` varchar (200) DEFAULT '' not null,
`avatar` varchar (100) DEFAULT '' not null, `avatar` varchar (100) DEFAULT '' not null,
`message` text DEFAULT '' not null, `message` text DEFAULT '' not null,

View File

@ -7,6 +7,7 @@
<div class="colstack_item rowmenu"> <div class="colstack_item rowmenu">
<div class="rowitem passive"><a href="/user/edit/password/">{{lang "account_menu_password"}}</a></div> <div class="rowitem passive"><a href="/user/edit/password/">{{lang "account_menu_password"}}</a></div>
<div class="rowitem passive"><a href="/user/edit/email/">{{lang "account_menu_email"}}</a></div> <div class="rowitem passive"><a href="/user/edit/email/">{{lang "account_menu_email"}}</a></div>
<div class="rowitem passive"><a href="/user/edit/privacy/">{{lang "account_menu_privacy"}}</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/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/logins/">{{lang "account_menu_logins"}}</a></div>
<div class="rowitem passive"><a href="/user/edit/blocked/">{{lang "account_menu_blocked"}}</a></div> <div class="rowitem passive"><a href="/user/edit/blocked/">{{lang "account_menu_blocked"}}</a></div>

View File

@ -0,0 +1,34 @@
<div class="colstack_item colstack_head rowhead">
<div class="rowitem"><h1>{{lang "account_privacy_head"}}</h1></div>
</div>
<div class="colstack_item the_form">
<form action="/user/edit/privacy/submit/?s={{.CurrentUser.Session}}" method="post">
<!--<input name="o_profile_comments" value="{{if .ProfileComments}}1{{else}}0{{end}}" type="hidden" />
<input name="o_receive_convos" value="{{if .ReceiveConvos}}1{{else}}0{{end}}" type="hidden" />-->
<input name="o_enable_embeds" value="{{if .EnableEmbeds}}1{{else}}0{{end}}" type="hidden" />
<!--<div class="formrow real_first_child">
<div class="formitem formlabel">{{lang "account_privacy_profile_comments"}}</div>
<div class="formitem"><select name="profile_comments">
<option{{if .ProfileComments}} selected{{end}} value=1>{{lang "option_yes"}}</option>
<option{{if not .ProfileComments}} selected{{end}} value=0>{{lang "option_no"}}</option>
</select></div>
</div>
<div class="formrow">
<div class="formitem formlabel"><a>Receive Conversations</a></div>
<div class="formitem"><select name="receive_convos">
<option{{if .ReceiveConvos}} selected{{end}} value=1>{{lang "option_yes"}}</option>
<option{{if not .ReceiveConvos}} selected{{end}} value=0>{{lang "option_no"}}</option>
</select></div>
</div>-->
<div class="formrow">
<div class="formitem formlabel"><a>Enable Embeds</a></div>
<div class="formitem"><select name="enable_embeds">
<option{{if .EnableEmbeds}} selected{{end}} value=1>{{lang "option_yes"}}</option>
<option{{if not .EnableEmbeds}} selected{{end}} value=0>{{lang "option_no"}}</option>
</select></div>
</div>
<div class="formrow">
<div class="formitem"><button name="account-button" class="formbutton form_middle_button">{{lang "account_privacy_button"}}</button></div>
</div>
</form>
</div>