diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go
index 4b35b0e1..f125e67c 100644
--- a/cmd/query_gen/tables.go
+++ b/cmd/query_gen/tables.go
@@ -31,11 +31,15 @@ func createTables(adapter qgen.Adapter) (err error) {
tC{"session", "varchar", 200, false, false, "''"},
//tC{"authToken", "varchar", 200, false, false, "''"},
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{"avatar", "varchar", 100, false, false, "''"},
tC{"message", "text", 0, false, false, "''"},
+
+ // TODO: Drop these columns?
tC{"url_prefix", "varchar", 20, false, false, "''"},
tC{"url_name", "varchar", 100, false, false, "''"},
+
tC{"level", "smallint", 0, false, false, "0"},
tC{"score", "int", 0, false, false, "0"},
tC{"posts", "int", 0, false, false, "0"},
diff --git a/common/pages.go b/common/pages.go
index 321e35e4..d5aa7bf2 100644
--- a/common/pages.go
+++ b/common/pages.go
@@ -248,6 +248,13 @@ type AccountBlocksPage struct {
Paginator
}
+type AccountPrivacyPage struct {
+ *Header
+ ProfileComments bool
+ ReceiveConvos bool
+ EnableEmbeds bool
+}
+
type AccountDashPage struct {
*Header
MFASetup bool
diff --git a/common/parser.go b/common/parser.go
index c58660c6..8616986b 100644
--- a/common/parser.go
+++ b/common/parser.go
@@ -449,10 +449,26 @@ var hashLinkMap = map[string]func(*strings.Builder, string, *int){
// 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: 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?
-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
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--
} else if msg[i] == 'h' || msg[i] == 'f' || msg[i] == 'g' || msg[i] == '/' {
//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] == '/' {
// Do nothing
- } else if len(msg) > i+5 && msg[i+4] == ':' && msg[i+5] == '/' {
+ } else if msg[i+4] == ':' && msg[i+5] == '/' {
// Do nothing
} else {
continue
@@ -593,7 +609,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
continue
}
- media, ok := parseMediaString(msg[i : i+urlLen])
+ media, ok := parseMediaString(msg[i:i+urlLen], settings)
if !ok {
//fmt.Println("o3")
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.
for ; len(data) > i; i++ {
- ch := data[i] // char
- 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 [
+ ch := data[i]
+ 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
}
}
@@ -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.
for ; datalen > i; i++ {
- ch := data[i] // char
- 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 [
+ ch := data[i]
+ 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
}
}
@@ -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.
for ; end >= i; i++ {
- ch := data[i] // char
- 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 [
+ ch := data[i]
+ 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
}
}
@@ -796,7 +812,7 @@ func PartialURLStringLen(data string) (int, bool) {
if ch < 33 { // space and invisibles
//fmt.Println("e2:",i)
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)
//fmt.Println("e3")
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.
for ; len(data) > i; i++ {
- ch := data[i] //char
- 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 [
+ ch := data[i]
+ 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)
return i
}
@@ -850,7 +866,7 @@ type MediaEmbed struct {
}
// 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) {
return media, false
}
@@ -907,32 +923,34 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) {
}
}
- // ? - 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
- if strings.HasSuffix(host, ".youtube.com") && path == "/watch" {
- video, ok := query["v"]
- if ok && len(video) >= 1 && video[0] != "" {
- media.Type = "raw"
- // TODO: Filter the URL to make sure no nasties end up in there
- media.Body = ""
- return media, true
- }
- }
-
- if lastFrag := pathFrags[len(pathFrags)-1]; lastFrag != "" {
- // TODO: Write a function for getting the file extension of a string
- if extarr := strings.Split(lastFrag, "."); len(extarr) >= 2 {
- ext := extarr[len(extarr)-1]
- if ImageFileExts.Contains(ext) {
- media.Type = "image"
- var sport string
- if port != "443" && port != "80" && port != "" {
- sport = ":" + port
- }
- media.URL = scheme + "//" + host + sport + path
+ if !settings.NoEmbed {
+ // ? - 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
+ if strings.HasSuffix(host, ".youtube.com") && path == "/watch" {
+ video, ok := query["v"]
+ if ok && len(video) >= 1 && video[0] != "" {
+ media.Type = "raw"
+ // TODO: Filter the URL to make sure no nasties end up in there
+ media.Body = ""
return media, true
}
}
+
+ if lastFrag := pathFrags[len(pathFrags)-1]; lastFrag != "" {
+ // TODO: Write a function for getting the file extension of a string
+ if extarr := strings.Split(lastFrag, "."); len(extarr) >= 2 {
+ ext := extarr[len(extarr)-1]
+ if ImageFileExts.Contains(ext) {
+ media.Type = "image"
+ var sport string
+ if port != "443" && port != "80" && port != "" {
+ sport = ":" + port
+ }
+ media.URL = scheme + "//" + host + sport + path
+ return media, true
+ }
+ }
+ }
}
var sport string
@@ -947,8 +965,8 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) {
if len(uurl.Fragment) > 0 {
frag = "#" + uurl.Fragment
}
- media.URL = scheme + "//" + host + sport + path + q + frag
media.FURL = host + sport + path + q + frag
+ media.URL = scheme + "//" + media.FURL
return media, true
}
@@ -978,7 +996,7 @@ func CoerceIntString(data string) (res int, length int) {
// TODO: Write tests for this
// 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
pre := 3
if diff < 3 {
@@ -998,7 +1016,7 @@ func Paginate(currentPage int, lastPage int, maxPages int) (out []int) {
// TODO: Write tests for this
// 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
lastPage := LastPage(count, perPage)
if page > 1 {
@@ -1020,6 +1038,6 @@ func PageOffset(count int, page int, perPage int) (int, int, int) {
// TODO: Write tests for this
// 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
}
diff --git a/common/profile_reply.go b/common/profile_reply.go
index fcac0fc0..e4ac4ce2 100644
--- a/common/profile_reply.go
+++ b/common/profile_reply.go
@@ -5,7 +5,7 @@ import (
"html"
"time"
- "github.com/Azareal/Gosora/query_gen"
+ qgen "github.com/Azareal/Gosora/query_gen"
)
var profileReplyStmts ProfileReplyStmts
@@ -20,7 +20,7 @@ type ProfileReply struct {
LastEdit int
LastEditBy int
ContentLines int
- IP string
+ IP string
}
type ProfileReplyStmts struct {
@@ -30,9 +30,10 @@ type ProfileReplyStmts struct {
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
+ ur := "users_replies"
profileReplyStmts = ProfileReplyStmts{
- edit: acc.Update("users_replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(),
- delete: acc.Delete("users_replies").Where("rid = ?").Prepare(),
+ edit: acc.Update(ur).Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(),
+ delete: acc.Delete(ur).Where("rid = ?").Prepare(),
}
return acc.FirstError()
})
@@ -51,7 +52,7 @@ func (r *ProfileReply) Delete() error {
func (r *ProfileReply) SetBody(content string) error {
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
}
diff --git a/common/profile_reply_store.go b/common/profile_reply_store.go
index 908ad755..a7d67212 100644
--- a/common/profile_reply_store.go
+++ b/common/profile_reply_store.go
@@ -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) {
- 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 {
return 0, err
}
diff --git a/common/reply.go b/common/reply.go
index 03c4c31a..0c8441e6 100644
--- a/common/reply.go
+++ b/common/reply.go
@@ -17,31 +17,18 @@ import (
type ReplyUser struct {
Reply
- //ID int
- //ParentID int
- //Content string
- ContentHtml string
- //CreatedBy int
+
+ ContentHtml string
UserLink string
CreatedByName string
- //Group int
- //CreatedAt time.Time
- //LastEdit int
- //LastEditBy int
- Avatar string
- MicroAvatar string
- ClassName string
- //ContentLines int
- Tag string
- URL string
- URLPrefix string
- URLName string
- Level int
- //IP string
- //Liked bool
- //LikeCount int
- //AttachCount int
- //ActionType string
+ Avatar string
+ MicroAvatar string
+ ClassName string
+ Tag string
+ URL string
+ //URLPrefix string
+ //URLName string
+ Level int
ActionIcon string
Attachments []*MiniAttachment
@@ -139,7 +126,7 @@ func (r *Reply) SetPost(content string) error {
return err
}
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
_ = Rstore.GetCache().Remove(r.ID)
return err
diff --git a/common/reply_store.go b/common/reply_store.go
index bbaa3348..99305f12 100644
--- a/common/reply_store.go
+++ b/common/reply_store.go
@@ -52,7 +52,7 @@ func (s *SQLReplyStore) Get(id int) (*Reply, error) {
// TODO: Write a test for this
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 {
return 0, err
}
diff --git a/common/report_store.go b/common/report_store.go
index 43bb765b..3a21f7cd 100644
--- a/common/report_store.go
+++ b/common/report_store.go
@@ -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
-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
err = s.exists.QueryRow(itemType+"_"+strconv.Itoa(itemID), ReportForumID).Scan(&count)
if err != nil && err != sql.ErrNoRows {
@@ -44,7 +44,7 @@ func (s *DefaultReportStore) Create(title string, content string, user *User, it
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 {
return 0, err
}
@@ -53,5 +53,6 @@ func (s *DefaultReportStore) Create(title string, content string, user *User, it
return 0, err
}
tid = int(lastID)
- return tid, Forums.AddTopic(tid, user.ID, ReportForumID)
+
+ return tid, Forums.AddTopic(tid, u.ID, ReportForumID)
}
diff --git a/common/site.go b/common/site.go
index 0f3e1888..5cf39006 100644
--- a/common/site.go
+++ b/common/site.go
@@ -91,6 +91,7 @@ type config struct {
PrimaryServer bool
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
LogPruneCutoff int
@@ -99,7 +100,7 @@ type config struct {
//LooseCSP bool
LooseHost bool
LoosePort bool
- SslSchema bool // Pretend we're using SSL, might be useful if a reverse-proxy terminates SSL in-front of Gosora
+ SslSchema bool // Pretend we're using SSL, might be useful if a reverse-proxy terminates SSL in-front of Gosora
DisableServerPush bool
EnableCDNPush bool
DisableNoavatarRange bool
@@ -107,6 +108,7 @@ type config struct {
RefNoTrack bool
RefNoRef bool
+ NoEmbed bool
Noavatar string // ? - 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) {
Config.Noavatar = strings.Replace(Config.Noavatar, "{site_url}", Site.URL, -1)
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.LocalHost = Site.Host == "localhost" || Site.Host == "127.0.0.1" || Site.Host == "::1"
Site.PortInt, err = strconv.Atoi(Site.Port)
@@ -216,6 +222,9 @@ func ProcessConfig() (err error) {
if Config.LogPruneCutoff == 0 {
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
if Config.MaxTopicTitleLength == 0 {
@@ -240,20 +249,18 @@ func ProcessConfig() (err error) {
return nil
}
-func VerifyConfig() error {
- if !Forums.Exists(Config.DefaultForum) {
- return errors.New("Invalid default forum")
+func VerifyConfig() (err error) {
+ switch {
+ 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 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
+ return err
}
func SwitchToTestDB() {
diff --git a/common/template_init.go b/common/template_init.go
index bc782c8a..e97862ee 100644
--- a/common/template_init.go
+++ b/common/template_init.go
@@ -14,8 +14,8 @@ import (
"github.com/Azareal/Gosora/common/alerts"
p "github.com/Azareal/Gosora/common/phrases"
- "github.com/Azareal/Gosora/common/templates"
- "github.com/Azareal/Gosora/query_gen"
+ tmpl "github.com/Azareal/Gosora/common/templates"
+ qgen "github.com/Azareal/Gosora/query_gen"
)
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) {
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?
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, "")
- 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
}
@@ -170,8 +170,8 @@ func CompileTemplates() error {
log.Printf("overriden: %+v\n", overriden)
config := tmpl.CTemplateConfig{
- Minify: Config.MinifyTemplates,
- Debug: Dev.DebugMode,
+ Minify: Config.MinifyTemplates,
+ Debug: Dev.DebugMode,
SuperDebug: Dev.TemplateDebug,
}
c := tmpl.NewCTemplateSet("normal")
@@ -239,11 +239,11 @@ func compileCommons(c *tmpl.CTemplateSet, head *Header, head2 *Header, forumList
}, VoteCount: 7}
avatar, microAvatar := BuildAvatar(62, "")
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
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()
replyList = append(replyList, ru)
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?
//avatar, microAvatar = BuildAvatar(0, "")
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()
replyList = append(replyList, ru)
@@ -296,7 +296,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
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)
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}}
// Experimental!
- for _, tmpl := range strings.Split(Dev.ExtraTmpls,",") {
- sp := strings.Split(tmpl,":")
+ for _, tmpl := range strings.Split(Dev.ExtraTmpls, ",") {
+ sp := strings.Split(tmpl, ":")
if len(sp) < 2 {
continue
}
@@ -350,27 +350,27 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
t.AddStd("account", "c.Account", accountPage)
parti := []*User{&user}
- convo := &Conversation{1,user.ID,time.Now(),0,time.Now()}
- convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1,1,"hey","",user.ID}, &user, "", 4, true}}
+ convo := &Conversation{1, user.ID, time.Now(), 0, time.Now()}
+ convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}}
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}}
t.AddStd("convos", "c.ConvoListPage", convoListPage)
basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID}
t.AddStd("panel", "c.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter})
- ges := []GridElement{GridElement{"","", "", 1, "grid_istat", "", "", ""}}
- t.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges,ges})
+ ges := []GridElement{GridElement{"", "", "", 1, "grid_istat", "", "", ""}}
+ t.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges, ges})
goVersion := runtime.Version()
dbVersion := qgen.Builder.DbVersion()
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
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}
- debugDisk := DebugPageDisk{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}
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_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter})
@@ -456,13 +456,13 @@ func CompileJSTemplates() error {
log.Printf("overriden: %+v\n", overriden)
config := tmpl.CTemplateConfig{
- Minify: Config.MinifyTemplates,
- Debug: Dev.DebugMode,
- SuperDebug: Dev.TemplateDebug,
- SkipHandles: true,
+ Minify: Config.MinifyTemplates,
+ Debug: Dev.DebugMode,
+ SuperDebug: Dev.TemplateDebug,
+ SkipHandles: true,
SkipTmplPtrMap: true,
- SkipInitBlock: false,
- PackageName: "tmpl",
+ SkipInitBlock: false,
+ PackageName: "tmpl",
}
c := tmpl.NewCTemplateSet("js")
c.SetConfig(config)
@@ -531,12 +531,12 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
}, VoteCount: 7}
avatar, microAvatar := BuildAvatar(62, "")
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
// TODO: Do we really want the UID here to be zero?
avatar, microAvatar = BuildAvatar(0, "")
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()
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_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}
- convo := &Conversation{1,user.ID,time.Now(),0,time.Now()}
- convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1,1,"hey","",user.ID}, &user, "", 4, true}}
+ convo := &Conversation{1, user.ID, time.Now(), 0, time.Now()}
+ convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1, 1, "hey", "", user.ID}, &user, "", 4, true}}
convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}}
t.AddStd("convo", "c.ConvoViewPage", convoPage)
@@ -825,7 +825,7 @@ func loadTemplates(t *template.Template, themeName string) error {
if err != nil {
return err
}
-
+
tFileMap := make(map[string]int)
for index, path := range tFiles {
path = strings.Replace(path, "\\", "/", -1)
diff --git a/common/topic.go b/common/topic.go
index 7045b6c1..af6608f4 100644
--- a/common/topic.go
+++ b/common/topic.go
@@ -11,7 +11,6 @@ import (
"html"
"html/template"
- //"log"
"strconv"
"strings"
"time"
@@ -83,10 +82,10 @@ type TopicUser struct {
ContentHTML string // TODO: Avoid converting this to bytes in templates, particularly if it's long
Tag string
URL string
- URLPrefix string
- URLName string
- Level int
- Liked bool
+ //URLPrefix string
+ //URLName string
+ Level int
+ Liked bool
Attachments []*MiniAttachment
Rids []int
@@ -215,7 +214,7 @@ func init() {
t := "topics"
topicStmts = TopicStmts{
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(),
updateLastReply: acc.Update(t).Set("lastReplyID = ?").Where("lastReplyID > ? AND 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(),
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 = ?", "", ""),
}
return acc.FirstError()
@@ -250,7 +249,7 @@ func (t *Topic) cacheRemove() {
}
// 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)
if err != nil {
return err
@@ -300,7 +299,7 @@ func (t *Topic) Unstick() (err error) {
// TODO: Test 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
err = topicStmts.hasLikedTopic.QueryRow(uid, t.ID).Scan(&disp)
if err != nil && err != ErrNoRows {
@@ -308,12 +307,10 @@ func (t *Topic) Like(score int, uid int) (err error) {
} else if err != ErrNoRows {
return ErrAlreadyLiked
}
-
_, err = topicStmts.createLike.Exec(score, t.ID, "topics", uid)
if err != nil {
return err
}
-
_, err = topicStmts.addLikesToTopic.Exec(1, t.ID)
if err != nil {
return err
@@ -360,7 +357,7 @@ func (t *Topic) Delete() error {
}
// 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))
if name == "" {
return ErrNoTitle
@@ -371,7 +368,7 @@ func (t *Topic) Update(name string, content string) error {
}
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)
t.cacheRemove()
return err
@@ -404,7 +401,7 @@ func (t *Topic) CreateActionReply(action string, ip string, uid int) (err error)
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)
if err != nil {
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...
if ru.ActionType != "" {
- var action string
aarr := strings.Split(ru.ActionType, "-")
- switch aarr[0] {
+ action := aarr[0]
+ switch action {
case "lock":
- action = aarr[0]
ru.ActionIcon = lockai
case "unlock":
- action = aarr[0]
ru.ActionIcon = unlockai
case "stick":
- action = aarr[0]
ru.ActionIcon = stickai
case "unstick":
- action = aarr[0]
ru.ActionIcon = unstickai
case "move":
if len(aarr) == 2 {
@@ -471,19 +464,15 @@ func (ru *ReplyUser) Init() error {
forum, err := Forums.Get(fid)
if err == nil {
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_move_dest", forum.Link, forum.Name, ru.UserLink, ru.CreatedByName)
- } else {
- action = aarr[0]
+ return nil
}
- } else {
- action = aarr[0]
}
default:
// 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)
+ 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
@@ -516,42 +505,52 @@ func (t *TopicUser) Replies(offset int, pFrag int, user *User) (rlist []*ReplyUs
ruser, err = ucache.Get(re.CreatedBy)
}
- // TODO: Factor the user fields out and embed a user struct instead
- var reply *ReplyUser
hTbl := GetHookTable()
- 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 := reply.Init()
+ rf := func(r *ReplyUser) error {
+ err := r.Init()
if err != nil {
- return nil, "", 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
+ return err
}
- if reply.ID == pFrag {
- ogdesc = reply.Content
+ r.ContentHtml = ParseMessage(r.Content, t.ParentID, "forums", user.ParseSettings)
+ // 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 {
ogdesc = ogdesc[:197] + "..."
}
}
- if reply.LikeCount > 0 && user.Liked > 0 {
- likedMap[reply.ID] = len(rlist)
- likedQueryList = append(likedQueryList, reply.ID)
+ if r.LikeCount > 0 && user.Liked > 0 {
+ likedMap[r.ID] = len(rlist)
+ likedQueryList = append(likedQueryList, r.ID)
}
- if user.Perms.EditReply && reply.AttachCount > 0 {
- attachMap[reply.ID] = len(rlist)
- attachQueryList = append(attachQueryList, reply.ID)
+ if user.Perms.EditReply && r.AttachCount > 0 {
+ attachMap[r.ID] = len(rlist)
+ 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)
- rlist = append(rlist, reply)
+ hTbl.VhookNoRet("topic_reply_row_assign", &rlist, &r)
+ // 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 {
rows, err := topicStmts.getReplies.Query(t.ID, offset, Config.ItemsPerPage)
if err != nil {
@@ -560,39 +559,15 @@ func (t *TopicUser) Replies(offset int, pFrag int, user *User) (rlist []*ReplyUs
defer rows.Close()
for rows.Next() {
- reply = &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)
+ r := &ReplyUser{}
+ 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 {
return nil, "", err
}
- if err := reply.Init(); err != nil {
+ err = rf(r)
+ if err != nil {
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()
if err != nil {
@@ -689,7 +664,7 @@ func GetTopicUser(user *User, tid int) (tu TopicUser, err error) {
tu = TopicUser{ID: tid}
// 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.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
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.Avatar = u.Avatar
tu.MicroAvatar = u.MicroAvatar
- tu.URLPrefix = u.URLPrefix
- tu.URLName = u.URLName
+ //tu.URLPrefix = u.URLPrefix
+ //tu.URLName = u.URLName
tu.Level = u.Level
tu.ID = t.ID
diff --git a/common/topic_store.go b/common/topic_store.go
index 158e52e3..845da1c6 100644
--- a/common/topic_store.go
+++ b/common/topic_store.go
@@ -29,7 +29,7 @@ type TopicStore interface {
BypassGet(id int) (*Topic, error)
BulkGetMap(ids []int) (list map[int]*Topic, err error)
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
Reload(id int) error // Too much SQL logic to move into TopicCache
// TODO: Implement these two methods
@@ -197,22 +197,22 @@ func (s *DefaultTopicStore) Exists(id int) bool {
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) {
- if topicName == "" {
+func (s *DefaultTopicStore) Create(fid int, name string, content string, uid int, ip string) (tid int, err error) {
+ if name == "" {
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?
- if len(topicName) > Config.MaxTopicTitleLength {
+ if len(name) > Config.MaxTopicTitleLength {
return 0, ErrLongTitle
}
- parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums"))
+ parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums", nil))
if parsedContent == "" {
return 0, ErrNoBody
}
// 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 {
return 0, err
}
diff --git a/common/user.go b/common/user.go
index 3eef6284..e03081c3 100644
--- a/common/user.go
+++ b/common/user.go
@@ -46,16 +46,19 @@ type User struct {
Avatar string
MicroAvatar string
Message string
- URLPrefix string // Move this to another table? Create a user lite?
- URLName string
- Tag string
- Level int
- Score int
- Posts int
- Liked int
- LastIP string // ! This part of the UserCache data might fall out of date
- LastAgent string // ! Temporary hack, don't use
- TempGroup int
+ // TODO: Implement something like this for profiles?
+ //URLPrefix string // Move this to another table? Create a user lite?
+ //URLName string
+ Tag string
+ Level int
+ Score int
+ Posts int
+ Liked int
+ LastIP string // ! This part of the UserCache data might fall out of date
+ LastAgent string // ! Temporary hack, don't use
+ TempGroup int
+
+ ParseSettings *ParseSettings
}
func (u *User) WebSockets() *WsJSONUser {
@@ -119,7 +122,7 @@ type UserStmts struct {
changeGroup *sql.Stmt
delete *sql.Stmt
setAvatar *sql.Stmt
- setUsername *sql.Stmt
+ setName *sql.Stmt
incTopics *sql.Stmt
updateLevel *sql.Stmt
update *sql.Stmt
@@ -131,8 +134,9 @@ type UserStmts struct {
incMegaposts *sql.Stmt
incLiked *sql.Stmt
- decLiked *sql.Stmt
- updateLastIP *sql.Stmt
+ decLiked *sql.Stmt
+ updateLastIP *sql.Stmt
+ updatePrivacy *sql.Stmt
setPassword *sql.Stmt
@@ -143,27 +147,29 @@ var userStmts UserStmts
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
- w := "uid = ?"
+ u := "users"
+ w := "uid=?"
userStmts = UserStmts{
- activate: acc.SimpleUpdate("users", "active = 1", w),
- changeGroup: acc.SimpleUpdate("users", "group = ?", w), // TODO: Implement user_count for users_groups here
- delete: acc.SimpleDelete("users", w),
- setAvatar: acc.Update("users").Set("avatar = ?").Where(w).Prepare(),
- setUsername: acc.Update("users").Set("name = ?").Where(w).Prepare(),
- incTopics: acc.SimpleUpdate("users", "topics = topics + ?", w),
- updateLevel: acc.SimpleUpdate("users", "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
+ activate: acc.SimpleUpdate(u, "active=1", w),
+ changeGroup: acc.SimpleUpdate(u, "group=?", w), // TODO: Implement user_count for users_groups here
+ delete: acc.Delete(u).Where(w).Prepare(),
+ setAvatar: acc.Update(u).Set("avatar=?").Where(w).Prepare(),
+ setName: acc.Update(u).Set("name=?").Where(w).Prepare(),
+ incTopics: acc.SimpleUpdate(u, "topics=topics+?", w),
+ updateLevel: acc.SimpleUpdate(u, "level=?", w),
+ 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(),
- incPosts: acc.Update("users").Set("posts = posts + ?").Where(w).Prepare(),
- incBigposts: acc.Update("users").Set("posts = posts + ?, bigposts = bigposts + ?").Where(w).Prepare(),
- incMegaposts: acc.Update("users").Set("posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?").Where(w).Prepare(),
- incLiked: acc.Update("users").Set("liked = liked + ?, lastLiked = UTC_TIMESTAMP()").Where(w).Prepare(),
- decLiked: acc.Update("users").Set("liked = liked - ?").Where(w).Prepare(),
+ incScore: acc.Update(u).Set("score=score+?").Where(w).Prepare(),
+ incPosts: acc.Update(u).Set("posts=posts+?").Where(w).Prepare(),
+ incBigposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?").Where(w).Prepare(),
+ incMegaposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?,megaposts=megaposts+?").Where(w).Prepare(),
+ incLiked: acc.Update(u).Set("liked=liked+?,lastLiked=UTC_TIMESTAMP()").Where(w).Prepare(),
+ decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(),
//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(),
}
@@ -204,7 +210,7 @@ func (u *User) deleteScheduleGroupTx(tx *sql.Tx) 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 {
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) {
- return u.bindStmt(userStmts.setUsername, name)
+ return u.bindStmt(userStmts.setName, name)
}
func (u *User) ChangeAvatar(avatar string) (err error) {
@@ -341,7 +347,15 @@ func (u *User) UpdateIP(host string) error {
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)
}
@@ -461,7 +475,7 @@ type GuestAvatar struct {
Micro string
}
-func buildNoavatar(uid int, width int) string {
+func buildNoavatar(uid, width int) string {
if !Config.DisableNoavatarRange {
// TODO: Find a faster algorithm
if uid > 50000 {
@@ -520,7 +534,6 @@ func wordsToScore(wcount int, topic bool) (score int) {
} else {
score = 1
}
-
settings := SettingBox.Load().(SettingMap)
if wcount >= settings["megapost_min_words"].(int) {
score += 4
diff --git a/common/user_store.go b/common/user_store.go
index 89ccfe60..fb50c71e 100644
--- a/common/user_store.go
+++ b/common/user_store.go
@@ -5,7 +5,7 @@ import (
"errors"
"strconv"
- "github.com/Azareal/Gosora/query_gen"
+ qgen "github.com/Azareal/Gosora/query_gen"
"golang.org/x/crypto/bcrypt"
)
@@ -20,11 +20,11 @@ type UserStore interface {
Get(id int) (*User, error)
GetByName(name string) (*User, error)
Exists(id int) bool
- GetOffset(offset int, perPage int) (users []*User, err error)
+ GetOffset(offset, perPage int) ([]*User, error)
//BulkGet(ids []int) ([]*User, error)
BulkGetMap(ids []int) (map[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
Count() int
@@ -35,12 +35,12 @@ type UserStore interface {
type DefaultUserStore struct {
cache UserCache
- get *sql.Stmt
- getByName *sql.Stmt
- getOffset *sql.Stmt
- exists *sql.Stmt
- register *sql.Stmt
- usernameExists *sql.Stmt
+ get *sql.Stmt
+ getByName *sql.Stmt
+ getOffset *sql.Stmt
+ exists *sql.Stmt
+ register *sql.Stmt
+ nameExists *sql.Stmt
count *sql.Stmt
}
@@ -50,16 +50,17 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
if cache == nil {
cache = NewNullUserCache()
}
+ u := "users"
// TODO: Add an admin version of registerStmt with more flexibility?
return &DefaultUserStore{
- 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 = ?", "", ""),
- 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(),
- 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(),
- exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
- 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
- usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""),
- count: acc.Count("users").Prepare(),
+ cache: cache,
+ 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(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(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.Exists(u, "uid").Prepare(),
+ 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
+ nameExists: acc.Exists(u, "name").Prepare(),
+ count: acc.Count(u).Prepare(),
}, acc.FirstError()
}
@@ -86,8 +87,13 @@ func (s *DefaultUserStore) Get(id int) (*User, error) {
//log.Print("uncached user")
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 embeds != -1 {
+ u.ParseSettings = DefaultParseSettings.CopyPtr()
+ u.ParseSettings.NoEmbed = embeds == 0
+ }
u.Init()
s.cache.Set(u)
}
@@ -98,8 +104,13 @@ func (s *DefaultUserStore) Get(id int) (*User, error) {
// ! This bypasses the cache, use frugally
func (s *DefaultUserStore) GetByName(name string) (*User, error) {
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 embeds != -1 {
+ u.ParseSettings = DefaultParseSettings.CopyPtr()
+ u.ParseSettings.NoEmbed = embeds == 0
+ }
u.Init()
s.cache.Set(u)
}
@@ -115,12 +126,17 @@ func (s *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User, er
}
defer rows.Close()
+ var embeds int
for rows.Next() {
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 {
return nil, err
}
+ if embeds != -1 {
+ u.ParseSettings = DefaultParseSettings.CopyPtr()
+ u.ParseSettings.NoEmbed = embeds == 0
+ }
u.Init()
s.cache.Set(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
var q string
- idList := make([]interface{},len(ids))
+ idList := make([]interface{}, len(ids))
for i, id := range ids {
idList[i] = strconv.Itoa(id)
q += "?,"
}
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 {
return list, err
}
defer rows.Close()
+ var embeds int
for rows.Next() {
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 {
return list, err
}
+ if embeds != -1 {
+ u.ParseSettings = DefaultParseSettings.CopyPtr()
+ u.ParseSettings.NoEmbed = embeds == 0
+ }
u.Init()
s.cache.Set(u)
list[u.ID] = u
}
- err = rows.Err()
- if err != nil {
+ if err = rows.Err(); err != nil {
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) {
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 embeds != -1 {
+ u.ParseSettings = DefaultParseSettings.CopyPtr()
+ u.ParseSettings.NoEmbed = embeds == 0
+ }
u.Init()
}
return u, err
@@ -240,16 +265,16 @@ func (s *DefaultUserStore) Exists(id int) bool {
// TODO: Change active to a bool?
// 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?
// ? 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
}
- // Is this username already taken..?
- err := s.usernameExists.QueryRow(username).Scan(&username)
+ // Is this name already taken..?
+ err := s.nameExists.QueryRow(name).Scan(&name)
if err != ErrNoRows {
return 0, ErrAccountExists
}
@@ -262,7 +287,7 @@ func (s *DefaultUserStore) Create(username string, password string, email string
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 {
return 0, err
}
diff --git a/common/utils.go b/common/utils.go
index 63ff83f9..f5037906 100644
--- a/common/utils.go
+++ b/common/utils.go
@@ -31,12 +31,12 @@ type Version struct {
}
// TODO: Write a test for this
-func (version *Version) String() (out string) {
- out = strconv.Itoa(version.Major) + "." + strconv.Itoa(version.Minor) + "." + strconv.Itoa(version.Patch)
- if version.Tag != "" {
- out += "-" + version.Tag
- if version.TagID != 0 {
- out += strconv.Itoa(version.TagID)
+func (ver *Version) String() (out string) {
+ out = strconv.Itoa(ver.Major) + "." + strconv.Itoa(ver.Minor) + "." + strconv.Itoa(ver.Patch)
+ if ver.Tag != "" {
+ out += "-" + ver.Tag
+ if ver.TagID != 0 {
+ out += strconv.Itoa(ver.TagID)
}
}
return
@@ -335,10 +335,10 @@ func HasSuspiciousEmail(email string) bool {
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
-func WeakPassword(password string, username string, email string) error {
+func WeakPassword(password, username, email string) error {
lowPassword := strings.ToLower(password)
switch {
case password == "":
@@ -422,7 +422,7 @@ func createFile(name string) error {
}
// 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)
if err != nil {
return err
diff --git a/docs/configuration.md b/docs/configuration.md
index 5dbaa1fa..8fab52b8 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -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.
+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
ItemsPerPage - The number of posts, topics, etc. you want on each page.
diff --git a/extend/plugin_markdown.go b/extend/plugin_markdown.go
index 0e138523..8008dbc7 100644
--- a/extend/plugin_markdown.go
+++ b/extend/plugin_markdown.go
@@ -373,7 +373,6 @@ func markdownSkipList(data string, index int) int {
goto SkipListInnerLoop
}
}
-
if index >= datalen {
if data[index] != '*' && data[index] != '-' {
if (lastNewline + 1) < datalen {
diff --git a/gen_router.go b/gen_router.go
index cc0c98c4..8bcbd377 100644
--- a/gen_router.go
+++ b/gen_router.go
@@ -121,6 +121,8 @@ var RouteMap = map[string]interface{}{
"routes.AccountEditAvatarSubmit": routes.AccountEditAvatarSubmit,
"routes.AccountEditRevokeAvatarSubmit": routes.AccountEditRevokeAvatarSubmit,
"routes.AccountEditUsernameSubmit": routes.AccountEditUsernameSubmit,
+ "routes.AccountEditPrivacy": routes.AccountEditPrivacy,
+ "routes.AccountEditPrivacySubmit": routes.AccountEditPrivacySubmit,
"routes.AccountEditMFA": routes.AccountEditMFA,
"routes.AccountEditMFASetup": routes.AccountEditMFASetup,
"routes.AccountEditMFASetupSubmit": routes.AccountEditMFASetupSubmit,
@@ -290,73 +292,75 @@ var routeMapEnum = map[string]int{
"routes.AccountEditAvatarSubmit": 95,
"routes.AccountEditRevokeAvatarSubmit": 96,
"routes.AccountEditUsernameSubmit": 97,
- "routes.AccountEditMFA": 98,
- "routes.AccountEditMFASetup": 99,
- "routes.AccountEditMFASetupSubmit": 100,
- "routes.AccountEditMFADisableSubmit": 101,
- "routes.AccountEditEmail": 102,
- "routes.AccountEditEmailTokenSubmit": 103,
- "routes.AccountLogins": 104,
- "routes.AccountBlocked": 105,
- "routes.LevelList": 106,
- "routes.Convos": 107,
- "routes.ConvosCreate": 108,
- "routes.Convo": 109,
- "routes.ConvosCreateSubmit": 110,
- "routes.ConvosCreateReplySubmit": 111,
- "routes.ConvosDeleteReplySubmit": 112,
- "routes.ConvosEditReplySubmit": 113,
- "routes.RelationsBlockCreate": 114,
- "routes.RelationsBlockCreateSubmit": 115,
- "routes.RelationsBlockRemove": 116,
- "routes.RelationsBlockRemoveSubmit": 117,
- "routes.ViewProfile": 118,
- "routes.BanUserSubmit": 119,
- "routes.UnbanUser": 120,
- "routes.ActivateUser": 121,
- "routes.IPSearch": 122,
- "routes.CreateTopicSubmit": 123,
- "routes.EditTopicSubmit": 124,
- "routes.DeleteTopicSubmit": 125,
- "routes.StickTopicSubmit": 126,
- "routes.UnstickTopicSubmit": 127,
- "routes.LockTopicSubmit": 128,
- "routes.UnlockTopicSubmit": 129,
- "routes.MoveTopicSubmit": 130,
- "routes.LikeTopicSubmit": 131,
- "routes.AddAttachToTopicSubmit": 132,
- "routes.RemoveAttachFromTopicSubmit": 133,
- "routes.ViewTopic": 134,
- "routes.CreateReplySubmit": 135,
- "routes.ReplyEditSubmit": 136,
- "routes.ReplyDeleteSubmit": 137,
- "routes.ReplyLikeSubmit": 138,
- "routes.AddAttachToReplySubmit": 139,
- "routes.RemoveAttachFromReplySubmit": 140,
- "routes.ProfileReplyCreateSubmit": 141,
- "routes.ProfileReplyEditSubmit": 142,
- "routes.ProfileReplyDeleteSubmit": 143,
- "routes.PollVote": 144,
- "routes.PollResults": 145,
- "routes.AccountLogin": 146,
- "routes.AccountRegister": 147,
- "routes.AccountLogout": 148,
- "routes.AccountLoginSubmit": 149,
- "routes.AccountLoginMFAVerify": 150,
- "routes.AccountLoginMFAVerifySubmit": 151,
- "routes.AccountRegisterSubmit": 152,
- "routes.AccountPasswordReset": 153,
- "routes.AccountPasswordResetSubmit": 154,
- "routes.AccountPasswordResetToken": 155,
- "routes.AccountPasswordResetTokenSubmit": 156,
- "routes.DynamicRoute": 157,
- "routes.UploadedFile": 158,
- "routes.StaticFile": 159,
- "routes.RobotsTxt": 160,
- "routes.SitemapXml": 161,
- "routes.OpenSearchXml": 162,
- "routes.BadRoute": 163,
- "routes.HTTPSRedirect": 164,
+ "routes.AccountEditPrivacy": 98,
+ "routes.AccountEditPrivacySubmit": 99,
+ "routes.AccountEditMFA": 100,
+ "routes.AccountEditMFASetup": 101,
+ "routes.AccountEditMFASetupSubmit": 102,
+ "routes.AccountEditMFADisableSubmit": 103,
+ "routes.AccountEditEmail": 104,
+ "routes.AccountEditEmailTokenSubmit": 105,
+ "routes.AccountLogins": 106,
+ "routes.AccountBlocked": 107,
+ "routes.LevelList": 108,
+ "routes.Convos": 109,
+ "routes.ConvosCreate": 110,
+ "routes.Convo": 111,
+ "routes.ConvosCreateSubmit": 112,
+ "routes.ConvosCreateReplySubmit": 113,
+ "routes.ConvosDeleteReplySubmit": 114,
+ "routes.ConvosEditReplySubmit": 115,
+ "routes.RelationsBlockCreate": 116,
+ "routes.RelationsBlockCreateSubmit": 117,
+ "routes.RelationsBlockRemove": 118,
+ "routes.RelationsBlockRemoveSubmit": 119,
+ "routes.ViewProfile": 120,
+ "routes.BanUserSubmit": 121,
+ "routes.UnbanUser": 122,
+ "routes.ActivateUser": 123,
+ "routes.IPSearch": 124,
+ "routes.CreateTopicSubmit": 125,
+ "routes.EditTopicSubmit": 126,
+ "routes.DeleteTopicSubmit": 127,
+ "routes.StickTopicSubmit": 128,
+ "routes.UnstickTopicSubmit": 129,
+ "routes.LockTopicSubmit": 130,
+ "routes.UnlockTopicSubmit": 131,
+ "routes.MoveTopicSubmit": 132,
+ "routes.LikeTopicSubmit": 133,
+ "routes.AddAttachToTopicSubmit": 134,
+ "routes.RemoveAttachFromTopicSubmit": 135,
+ "routes.ViewTopic": 136,
+ "routes.CreateReplySubmit": 137,
+ "routes.ReplyEditSubmit": 138,
+ "routes.ReplyDeleteSubmit": 139,
+ "routes.ReplyLikeSubmit": 140,
+ "routes.AddAttachToReplySubmit": 141,
+ "routes.RemoveAttachFromReplySubmit": 142,
+ "routes.ProfileReplyCreateSubmit": 143,
+ "routes.ProfileReplyEditSubmit": 144,
+ "routes.ProfileReplyDeleteSubmit": 145,
+ "routes.PollVote": 146,
+ "routes.PollResults": 147,
+ "routes.AccountLogin": 148,
+ "routes.AccountRegister": 149,
+ "routes.AccountLogout": 150,
+ "routes.AccountLoginSubmit": 151,
+ "routes.AccountLoginMFAVerify": 152,
+ "routes.AccountLoginMFAVerifySubmit": 153,
+ "routes.AccountRegisterSubmit": 154,
+ "routes.AccountPasswordReset": 155,
+ "routes.AccountPasswordResetSubmit": 156,
+ "routes.AccountPasswordResetToken": 157,
+ "routes.AccountPasswordResetTokenSubmit": 158,
+ "routes.DynamicRoute": 159,
+ "routes.UploadedFile": 160,
+ "routes.StaticFile": 161,
+ "routes.RobotsTxt": 162,
+ "routes.SitemapXml": 163,
+ "routes.OpenSearchXml": 164,
+ "routes.BadRoute": 165,
+ "routes.HTTPSRedirect": 166,
}
var reverseRouteMapEnum = map[int]string{
0: "routes.Overview",
@@ -457,73 +461,75 @@ var reverseRouteMapEnum = map[int]string{
95: "routes.AccountEditAvatarSubmit",
96: "routes.AccountEditRevokeAvatarSubmit",
97: "routes.AccountEditUsernameSubmit",
- 98: "routes.AccountEditMFA",
- 99: "routes.AccountEditMFASetup",
- 100: "routes.AccountEditMFASetupSubmit",
- 101: "routes.AccountEditMFADisableSubmit",
- 102: "routes.AccountEditEmail",
- 103: "routes.AccountEditEmailTokenSubmit",
- 104: "routes.AccountLogins",
- 105: "routes.AccountBlocked",
- 106: "routes.LevelList",
- 107: "routes.Convos",
- 108: "routes.ConvosCreate",
- 109: "routes.Convo",
- 110: "routes.ConvosCreateSubmit",
- 111: "routes.ConvosCreateReplySubmit",
- 112: "routes.ConvosDeleteReplySubmit",
- 113: "routes.ConvosEditReplySubmit",
- 114: "routes.RelationsBlockCreate",
- 115: "routes.RelationsBlockCreateSubmit",
- 116: "routes.RelationsBlockRemove",
- 117: "routes.RelationsBlockRemoveSubmit",
- 118: "routes.ViewProfile",
- 119: "routes.BanUserSubmit",
- 120: "routes.UnbanUser",
- 121: "routes.ActivateUser",
- 122: "routes.IPSearch",
- 123: "routes.CreateTopicSubmit",
- 124: "routes.EditTopicSubmit",
- 125: "routes.DeleteTopicSubmit",
- 126: "routes.StickTopicSubmit",
- 127: "routes.UnstickTopicSubmit",
- 128: "routes.LockTopicSubmit",
- 129: "routes.UnlockTopicSubmit",
- 130: "routes.MoveTopicSubmit",
- 131: "routes.LikeTopicSubmit",
- 132: "routes.AddAttachToTopicSubmit",
- 133: "routes.RemoveAttachFromTopicSubmit",
- 134: "routes.ViewTopic",
- 135: "routes.CreateReplySubmit",
- 136: "routes.ReplyEditSubmit",
- 137: "routes.ReplyDeleteSubmit",
- 138: "routes.ReplyLikeSubmit",
- 139: "routes.AddAttachToReplySubmit",
- 140: "routes.RemoveAttachFromReplySubmit",
- 141: "routes.ProfileReplyCreateSubmit",
- 142: "routes.ProfileReplyEditSubmit",
- 143: "routes.ProfileReplyDeleteSubmit",
- 144: "routes.PollVote",
- 145: "routes.PollResults",
- 146: "routes.AccountLogin",
- 147: "routes.AccountRegister",
- 148: "routes.AccountLogout",
- 149: "routes.AccountLoginSubmit",
- 150: "routes.AccountLoginMFAVerify",
- 151: "routes.AccountLoginMFAVerifySubmit",
- 152: "routes.AccountRegisterSubmit",
- 153: "routes.AccountPasswordReset",
- 154: "routes.AccountPasswordResetSubmit",
- 155: "routes.AccountPasswordResetToken",
- 156: "routes.AccountPasswordResetTokenSubmit",
- 157: "routes.DynamicRoute",
- 158: "routes.UploadedFile",
- 159: "routes.StaticFile",
- 160: "routes.RobotsTxt",
- 161: "routes.SitemapXml",
- 162: "routes.OpenSearchXml",
- 163: "routes.BadRoute",
- 164: "routes.HTTPSRedirect",
+ 98: "routes.AccountEditPrivacy",
+ 99: "routes.AccountEditPrivacySubmit",
+ 100: "routes.AccountEditMFA",
+ 101: "routes.AccountEditMFASetup",
+ 102: "routes.AccountEditMFASetupSubmit",
+ 103: "routes.AccountEditMFADisableSubmit",
+ 104: "routes.AccountEditEmail",
+ 105: "routes.AccountEditEmailTokenSubmit",
+ 106: "routes.AccountLogins",
+ 107: "routes.AccountBlocked",
+ 108: "routes.LevelList",
+ 109: "routes.Convos",
+ 110: "routes.ConvosCreate",
+ 111: "routes.Convo",
+ 112: "routes.ConvosCreateSubmit",
+ 113: "routes.ConvosCreateReplySubmit",
+ 114: "routes.ConvosDeleteReplySubmit",
+ 115: "routes.ConvosEditReplySubmit",
+ 116: "routes.RelationsBlockCreate",
+ 117: "routes.RelationsBlockCreateSubmit",
+ 118: "routes.RelationsBlockRemove",
+ 119: "routes.RelationsBlockRemoveSubmit",
+ 120: "routes.ViewProfile",
+ 121: "routes.BanUserSubmit",
+ 122: "routes.UnbanUser",
+ 123: "routes.ActivateUser",
+ 124: "routes.IPSearch",
+ 125: "routes.CreateTopicSubmit",
+ 126: "routes.EditTopicSubmit",
+ 127: "routes.DeleteTopicSubmit",
+ 128: "routes.StickTopicSubmit",
+ 129: "routes.UnstickTopicSubmit",
+ 130: "routes.LockTopicSubmit",
+ 131: "routes.UnlockTopicSubmit",
+ 132: "routes.MoveTopicSubmit",
+ 133: "routes.LikeTopicSubmit",
+ 134: "routes.AddAttachToTopicSubmit",
+ 135: "routes.RemoveAttachFromTopicSubmit",
+ 136: "routes.ViewTopic",
+ 137: "routes.CreateReplySubmit",
+ 138: "routes.ReplyEditSubmit",
+ 139: "routes.ReplyDeleteSubmit",
+ 140: "routes.ReplyLikeSubmit",
+ 141: "routes.AddAttachToReplySubmit",
+ 142: "routes.RemoveAttachFromReplySubmit",
+ 143: "routes.ProfileReplyCreateSubmit",
+ 144: "routes.ProfileReplyEditSubmit",
+ 145: "routes.ProfileReplyDeleteSubmit",
+ 146: "routes.PollVote",
+ 147: "routes.PollResults",
+ 148: "routes.AccountLogin",
+ 149: "routes.AccountRegister",
+ 150: "routes.AccountLogout",
+ 151: "routes.AccountLoginSubmit",
+ 152: "routes.AccountLoginMFAVerify",
+ 153: "routes.AccountLoginMFAVerifySubmit",
+ 154: "routes.AccountRegisterSubmit",
+ 155: "routes.AccountPasswordReset",
+ 156: "routes.AccountPasswordResetSubmit",
+ 157: "routes.AccountPasswordResetToken",
+ 158: "routes.AccountPasswordResetTokenSubmit",
+ 159: "routes.DynamicRoute",
+ 160: "routes.UploadedFile",
+ 161: "routes.StaticFile",
+ 162: "routes.RobotsTxt",
+ 163: "routes.SitemapXml",
+ 164: "routes.OpenSearchXml",
+ 165: "routes.BadRoute",
+ 166: "routes.HTTPSRedirect",
}
var osMapEnum = map[string]int{
"unknown": 0,
@@ -681,7 +687,7 @@ type HTTPSRedirect struct {}
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Connection", "close")
- co.RouteViewCounter.Bump(164)
+ co.RouteViewCounter.Bump(166)
dest := "https://" + req.Host + req.URL.String()
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
}
@@ -889,7 +895,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
co.GlobalViewCounter.Bump()
if prefix == "/s" { //old prefix: /static
- co.RouteViewCounter.Bump(159)
+ co.RouteViewCounter.Bump(161)
req.URL.Path += extraData
routes.StaticFile(w, req)
return
@@ -1800,7 +1806,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
co.RouteViewCounter.Bump(97)
err = routes.AccountEditUsernameSubmit(w,req,user)
- case "/user/edit/mfa/":
+ case "/user/edit/privacy/":
err = c.MemberOnly(w,req,user)
if err != nil {
return err
@@ -1808,6 +1814,31 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
co.RouteViewCounter.Bump(98)
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 {
return err
}
@@ -1818,7 +1849,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(99)
+ co.RouteViewCounter.Bump(101)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1835,7 +1866,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(100)
+ co.RouteViewCounter.Bump(102)
err = routes.AccountEditMFASetupSubmit(w,req,user)
case "/user/edit/mfa/disable/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -1848,7 +1879,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(101)
+ co.RouteViewCounter.Bump(103)
err = routes.AccountEditMFADisableSubmit(w,req,user)
case "/user/edit/email/":
err = c.MemberOnly(w,req,user)
@@ -1856,14 +1887,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(102)
+ co.RouteViewCounter.Bump(104)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
}
err = routes.AccountEditEmail(w,req,user,head)
case "/user/edit/token/":
- co.RouteViewCounter.Bump(103)
+ co.RouteViewCounter.Bump(105)
err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData)
case "/user/edit/logins/":
err = c.MemberOnly(w,req,user)
@@ -1871,7 +1902,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(104)
+ co.RouteViewCounter.Bump(106)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1883,7 +1914,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(105)
+ co.RouteViewCounter.Bump(107)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1895,7 +1926,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(106)
+ co.RouteViewCounter.Bump(108)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1907,7 +1938,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(107)
+ co.RouteViewCounter.Bump(109)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1919,7 +1950,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(108)
+ co.RouteViewCounter.Bump(110)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1931,7 +1962,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(109)
+ co.RouteViewCounter.Bump(111)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -1948,7 +1979,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(110)
+ co.RouteViewCounter.Bump(112)
err = routes.ConvosCreateSubmit(w,req,user)
case "/user/convo/create/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -1961,7 +1992,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(111)
+ co.RouteViewCounter.Bump(113)
err = routes.ConvosCreateReplySubmit(w,req,user,extraData)
case "/user/convo/delete/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -1974,7 +2005,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(112)
+ co.RouteViewCounter.Bump(114)
err = routes.ConvosDeleteReplySubmit(w,req,user,extraData)
case "/user/convo/edit/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -1987,7 +2018,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(113)
+ co.RouteViewCounter.Bump(115)
err = routes.ConvosEditReplySubmit(w,req,user,extraData)
case "/user/block/create/":
err = c.MemberOnly(w,req,user)
@@ -1995,7 +2026,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(114)
+ co.RouteViewCounter.Bump(116)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2012,7 +2043,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(115)
+ co.RouteViewCounter.Bump(117)
err = routes.RelationsBlockCreateSubmit(w,req,user,extraData)
case "/user/block/remove/":
err = c.MemberOnly(w,req,user)
@@ -2020,7 +2051,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(116)
+ co.RouteViewCounter.Bump(118)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2037,11 +2068,11 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(117)
+ co.RouteViewCounter.Bump(119)
err = routes.RelationsBlockRemoveSubmit(w,req,user,extraData)
default:
req.URL.Path += extraData
- co.RouteViewCounter.Bump(118)
+ co.RouteViewCounter.Bump(120)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2061,7 +2092,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(119)
+ co.RouteViewCounter.Bump(121)
err = routes.BanUserSubmit(w,req,user,extraData)
case "/users/unban/":
err = c.NoSessionMismatch(w,req,user)
@@ -2074,7 +2105,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(120)
+ co.RouteViewCounter.Bump(122)
err = routes.UnbanUser(w,req,user,extraData)
case "/users/activate/":
err = c.NoSessionMismatch(w,req,user)
@@ -2087,7 +2118,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(121)
+ co.RouteViewCounter.Bump(123)
err = routes.ActivateUser(w,req,user,extraData)
case "/users/ips/":
err = c.MemberOnly(w,req,user)
@@ -2095,7 +2126,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(122)
+ co.RouteViewCounter.Bump(124)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2119,7 +2150,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(123)
+ co.RouteViewCounter.Bump(125)
err = routes.CreateTopicSubmit(w,req,user)
case "/topic/edit/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2132,7 +2163,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(124)
+ co.RouteViewCounter.Bump(126)
err = routes.EditTopicSubmit(w,req,user,extraData)
case "/topic/delete/submit/":
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
- co.RouteViewCounter.Bump(125)
+ co.RouteViewCounter.Bump(127)
err = routes.DeleteTopicSubmit(w,req,user)
case "/topic/stick/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2159,7 +2190,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(126)
+ co.RouteViewCounter.Bump(128)
err = routes.StickTopicSubmit(w,req,user,extraData)
case "/topic/unstick/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2172,7 +2203,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(127)
+ co.RouteViewCounter.Bump(129)
err = routes.UnstickTopicSubmit(w,req,user,extraData)
case "/topic/lock/submit/":
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
- co.RouteViewCounter.Bump(128)
+ co.RouteViewCounter.Bump(130)
err = routes.LockTopicSubmit(w,req,user)
case "/topic/unlock/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2199,7 +2230,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(129)
+ co.RouteViewCounter.Bump(131)
err = routes.UnlockTopicSubmit(w,req,user,extraData)
case "/topic/move/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2212,7 +2243,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(130)
+ co.RouteViewCounter.Bump(132)
err = routes.MoveTopicSubmit(w,req,user,extraData)
case "/topic/like/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2225,7 +2256,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(131)
+ co.RouteViewCounter.Bump(133)
err = routes.LikeTopicSubmit(w,req,user,extraData)
case "/topic/attach/add/submit/":
err = c.MemberOnly(w,req,user)
@@ -2242,7 +2273,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(132)
+ co.RouteViewCounter.Bump(134)
err = routes.AddAttachToTopicSubmit(w,req,user,extraData)
case "/topic/attach/remove/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2255,10 +2286,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(133)
+ co.RouteViewCounter.Bump(135)
err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData)
default:
- co.RouteViewCounter.Bump(134)
+ co.RouteViewCounter.Bump(136)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2282,7 +2313,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(135)
+ co.RouteViewCounter.Bump(137)
err = routes.CreateReplySubmit(w,req,user)
case "/reply/edit/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2295,7 +2326,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(136)
+ co.RouteViewCounter.Bump(138)
err = routes.ReplyEditSubmit(w,req,user,extraData)
case "/reply/delete/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2308,7 +2339,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(137)
+ co.RouteViewCounter.Bump(139)
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
case "/reply/like/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2321,7 +2352,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(138)
+ co.RouteViewCounter.Bump(140)
err = routes.ReplyLikeSubmit(w,req,user,extraData)
case "/reply/attach/add/submit/":
err = c.MemberOnly(w,req,user)
@@ -2338,7 +2369,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(139)
+ co.RouteViewCounter.Bump(141)
err = routes.AddAttachToReplySubmit(w,req,user,extraData)
case "/reply/attach/remove/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2351,7 +2382,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(140)
+ co.RouteViewCounter.Bump(142)
err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData)
}
case "/profile":
@@ -2367,7 +2398,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(141)
+ co.RouteViewCounter.Bump(143)
err = routes.ProfileReplyCreateSubmit(w,req,user)
case "/profile/reply/edit/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2380,7 +2411,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(142)
+ co.RouteViewCounter.Bump(144)
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
case "/profile/reply/delete/submit/":
err = c.NoSessionMismatch(w,req,user)
@@ -2393,7 +2424,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(143)
+ co.RouteViewCounter.Bump(145)
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
}
case "/poll":
@@ -2409,23 +2440,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(144)
+ co.RouteViewCounter.Bump(146)
err = routes.PollVote(w,req,user,extraData)
case "/poll/results/":
- co.RouteViewCounter.Bump(145)
+ co.RouteViewCounter.Bump(147)
err = routes.PollResults(w,req,user,extraData)
}
case "/accounts":
switch(req.URL.Path) {
case "/accounts/login/":
- co.RouteViewCounter.Bump(146)
+ co.RouteViewCounter.Bump(148)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
}
err = routes.AccountLogin(w,req,user,head)
case "/accounts/create/":
- co.RouteViewCounter.Bump(147)
+ co.RouteViewCounter.Bump(149)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2442,7 +2473,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(148)
+ co.RouteViewCounter.Bump(150)
err = routes.AccountLogout(w,req,user)
case "/accounts/login/submit/":
err = c.ParseForm(w,req,user)
@@ -2450,10 +2481,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(149)
+ co.RouteViewCounter.Bump(151)
err = routes.AccountLoginSubmit(w,req,user)
case "/accounts/mfa_verify/":
- co.RouteViewCounter.Bump(150)
+ co.RouteViewCounter.Bump(152)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2465,7 +2496,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(151)
+ co.RouteViewCounter.Bump(153)
err = routes.AccountLoginMFAVerifySubmit(w,req,user)
case "/accounts/create/submit/":
err = c.ParseForm(w,req,user)
@@ -2473,10 +2504,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(152)
+ co.RouteViewCounter.Bump(154)
err = routes.AccountRegisterSubmit(w,req,user)
case "/accounts/password-reset/":
- co.RouteViewCounter.Bump(153)
+ co.RouteViewCounter.Bump(155)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2488,10 +2519,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(154)
+ co.RouteViewCounter.Bump(156)
err = routes.AccountPasswordResetSubmit(w,req,user)
case "/accounts/password-reset/token/":
- co.RouteViewCounter.Bump(155)
+ co.RouteViewCounter.Bump(157)
head, err := c.UserCheck(w,req,&user)
if err != nil {
return err
@@ -2503,7 +2534,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
return err
}
- co.RouteViewCounter.Bump(156)
+ co.RouteViewCounter.Bump(158)
err = routes.AccountPasswordResetTokenSubmit(w,req,user)
}
/*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-Encoding")
}
- co.RouteViewCounter.Bump(158)
+ co.RouteViewCounter.Bump(160)
req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this?
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
switch(extraData) {
case "robots.txt":
- co.RouteViewCounter.Bump(160)
+ co.RouteViewCounter.Bump(162)
return routes.RobotsTxt(w,req)
case "favicon.ico":
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)
return nil
case "opensearch.xml":
- co.RouteViewCounter.Bump(162)
+ co.RouteViewCounter.Bump(164)
return routes.OpenSearchXml(w,req)
/*case "sitemap.xml":
- co.RouteViewCounter.Bump(161)
+ co.RouteViewCounter.Bump(163)
return routes.SitemapXml(w,req)*/
}
return c.NotFound(w,req,nil)
@@ -2558,7 +2589,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
r.RUnlock()
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
return handle(w,req,user)
}
@@ -2569,7 +2600,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
} else {
r.DumpRequest(req,"Bad Route")
}
- co.RouteViewCounter.Bump(163)
+ co.RouteViewCounter.Bump(165)
return c.NotFound(w,req,nil)
}
return err
diff --git a/general_test.go b/general_test.go
index 56b1f219..3a5c7853 100644
--- a/general_test.go
+++ b/general_test.go
@@ -791,7 +791,7 @@ func BenchmarkQueryTopicParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
var tu c.TopicUser
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 {
log.Fatal("No rows found!")
return
@@ -812,14 +812,14 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
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 {
b.Fatal(err)
}
defer getTopicUser.Close()
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 {
b.Fatal("No rows found!")
return
@@ -873,7 +873,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
var tu c.TopicUser
b.Run("topic", func(b *testing.B) {
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 {
b.Fatal("No rows found!")
return
@@ -885,7 +885,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
})
b.Run("topic_replies", func(b *testing.B) {
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 {
b.Fatal(err)
return
@@ -907,13 +907,13 @@ func BenchmarkQueriesSerial(b *testing.B) {
var group int
b.Run("topic_replies_scan", func(b *testing.B) {
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 {
b.Fatal(err)
return
}
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 {
b.Fatal(err)
return
@@ -933,36 +933,19 @@ func BenchmarkQueriesSerial(b *testing.B) {
// TODO: Take the attachment system into account in these parser benches
func BenchmarkParserSerial(b *testing.B) {
b.ReportAllocs()
- b.Run("empty_post", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = c.ParseMessage("", 0, "")
+ f := func(name, msg string) func(b *testing.B) {
+ return func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = 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, "")
- }
- })
- b.Run("five_smilies", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = c.ParseMessage("Hey everyone, how's it going? :):):):):)", 0, "")
- }
- })
- 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, "")
- }
- })
+ }
+ f("empty_post","")
+ f("short_post","Hey everyone, how's it going?")
+ f("one_smily","Hey everyone, how's it going? :)")
+ 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? :):):):):):):):):):):):):):):):):):):):)")
}
func BenchmarkBBCodePluginWithRegexpSerial(b *testing.B) {
diff --git a/parser_test.go b/parser_test.go
index 2d43805a..7c566fee 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -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.
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 != "" {
t.Error("Name: ", item.Name)
}
@@ -314,7 +314,7 @@ func TestParser(t *testing.T) {
l.Add("//"+c.Site.URL+"\n", ""+c.Site.URL+"
")
l.Add("//"+c.Site.URL+"\n//"+c.Site.URL, ""+c.Site.URL+"
"+c.Site.URL+"")
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 != "" {
t.Error("Name: ", item.Name)
}
@@ -338,7 +338,7 @@ func TestParser(t *testing.T) {
}
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 := "#nnid-1"
if res != expect {
t.Error("Bad output:", "'"+res+"'")
@@ -356,7 +356,7 @@ func TestParser(t *testing.T) {
}
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 = "#longidnameneedtooverflowhack-1"
if res != expect {
t.Error("Bad output:", "'"+res+"'")
diff --git a/patcher/patches.go b/patcher/patches.go
index 75dfc161..0ddfc672 100644
--- a/patcher/patches.go
+++ b/patcher/patches.go
@@ -42,6 +42,7 @@ func init() {
addPatch(25, patch25)
addPatch(26, patch26)
addPatch(27, patch27)
+ addPatch(28, patch28)
}
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))
}
+
+func patch28(scanner *bufio.Scanner) error {
+ return execStmt(qgen.Builder.AddColumn("users", tC{"enable_embeds", "int", 0, false, false, "-1"}, nil))
+}
\ No newline at end of file
diff --git a/router_gen/routes.go b/router_gen/routes.go
index 258089ee..b3fd1772 100644
--- a/router_gen/routes.go
+++ b/router_gen/routes.go
@@ -56,6 +56,8 @@ func userRoutes() *RouteGroup {
UploadAction("AvatarSubmit", "/avatar/submit/").MaxSizeVar("int(c.Config.MaxRequestSize)"),
Action("RevokeAvatarSubmit", "/avatar/revoke/submit/"),
Action("UsernameSubmit", "/username/submit/"), // TODO: Full test this
+ MView("Privacy", "/privacy/"),
+ Action("PrivacySubmit", "/privacy/submit/"),
MView("MFA", "/mfa/"),
MView("MFASetup", "/mfa/setup/"),
Action("MFASetupSubmit", "/mfa/setup/submit/"),
diff --git a/routes/account.go b/routes/account.go
index f2009dd5..3f9044af 100644
--- a/routes/account.go
+++ b/routes/account.go
@@ -14,7 +14,7 @@ import (
c "github.com/Azareal/Gosora/common"
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
@@ -153,7 +153,7 @@ func AccountLoginMFAVerify(w http.ResponseWriter, r *http.Request, user c.User,
if !mfaVerifySession(provSession, signedSession, uid) {
return c.LocalError("Invalid session", w, r, user)
}
-
+
return renderTemplate("login_mfa_verify", w, r, h, c.Page{h, tList, nil})
}
@@ -235,7 +235,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
if isNumeric(nameBits[0]) {
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")
}
@@ -423,15 +423,15 @@ func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c.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 {
return ferr
}
- ferr = c.ChangeAvatar("." + ext, w, r, user)
+ ferr = c.ChangeAvatar("."+ext, w, r, user)
if ferr != nil {
return ferr
}
-
+
// TODO: Only schedule a resize if the avatar isn't tiny
err := user.ScheduleAvatarResize()
if err != nil {
@@ -572,6 +572,37 @@ func AccountEditMFADisableSubmit(w http.ResponseWriter, r *http.Request, user c.
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 {
accountEditHead("account_email", w, r, &user, h)
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 {
email := r.PostFormValue("email")
-
_, err := c.Emails.Get(&user, email)
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 {
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)
if err != nil {
- return c.InternalError(err,w,r)
+ return c.InternalError(err, w, r)
}
if c.Site.EnableEmails {
err = c.SendValidationEmail(user.Name, email, token)
@@ -623,7 +653,7 @@ func AccountEditEmailAddSubmit(w http.ResponseWriter, r *http.Request, user c.Us
return c.LocalError(p.GetErrorPhrase("register_email_fail"), w, r, user)
}
}
-
+
http.Redirect(w, r, "/user/edit/email/?added=1", http.StatusSeeOther)
return nil
}
@@ -635,19 +665,19 @@ func AccountEditEmailRemoveSubmit(w http.ResponseWriter, r *http.Request, user c
// Quick and dirty check
_, err := c.Emails.Get(&user, email)
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 {
return c.InternalError(err, w, r)
}
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)
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)
return nil
}
@@ -729,13 +759,13 @@ func AccountBlocked(w http.ResponseWriter, r *http.Request, user c.User, h *c.He
for _, uid := range uids {
u, err := c.Users.Get(uid)
if err != nil {
- return c.InternalError(err,w,r)
+ return c.InternalError(err, w, r)
}
blocks = append(blocks, u)
}
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)
}
diff --git a/routes/profile.go b/routes/profile.go
index 8a2505d9..cbea1541 100644
--- a/routes/profile.go
+++ b/routes/profile.go
@@ -78,7 +78,7 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user c.User, header *c.
replyLiked := false
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()
group, err := c.Groups.Get(ru.Group)
diff --git a/routes/reply.go b/routes/reply.go
index ab5e471d..a6ba7a8c 100644
--- a/routes/reply.go
+++ b/routes/reply.go
@@ -194,7 +194,7 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
prid, _ := strconv.Atoi(r.FormValue("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 {
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 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(topic.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} 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 {
return c.InternalErrorJSQ(err, w, r, js)
}
diff --git a/routes/topic.go b/routes/topic.go
index aad18e67..8588b087 100644
--- a/routes/topic.go
+++ b/routes/topic.go
@@ -9,17 +9,17 @@ import (
"io"
//"fmt"
+ "golang.org/x/image/tiff"
+ "image"
+ "image/gif"
+ "image/jpeg"
+ "image/png"
"log"
"net/http"
"os"
"regexp"
"strconv"
"strings"
- "image"
- "image/gif"
- "image/jpeg"
- "image/png"
- "golang.org/x/image/tiff"
c "github.com/Azareal/Gosora/common"
"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)
// 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.
if topic.ContentHTML == topic.Content {
topic.ContentHTML = topic.Content
}
topic.ContentLines = strings.Count(topic.Content, "\n")
- header.OGDesc = topic.Content
- if len(header.OGDesc) > 200 {
- header.OGDesc = header.OGDesc[:197] + "..."
+ if len(topic.Content) > 200 {
+ header.OGDesc = topic.Content[:197] + "..."
+ } else {
+ header.OGDesc = topic.Content
}
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-") {
pFrag, _ = strconv.Atoi(strings.TrimPrefix(r.URL.Fragment, "post-"))
}
-
rlist, ogdesc, err := topic.Replies(offset, pFrag, &user)
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)
@@ -510,7 +510,7 @@ func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, user c.User, di
} else {
img, _, err := image.Decode(inFile)
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)
@@ -518,19 +518,19 @@ func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, user c.User, di
return nil, c.LocalError("Upload failed [File Creation Failed]", w, r, user)
}
defer outFile.Close()
-
+
switch ext {
case "gif":
err = gif.Encode(outFile, img, nil)
case "png":
err = png.Encode(outFile, img)
- case "tiff","tif":
- err = tiff.Encode(outFile,img,nil)
+ case "tiff", "tif":
+ err = tiff.Encode(outFile, img, nil)
default:
err = jpeg.Encode(outFile, img, 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 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
} 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 {
return c.InternalErrorJSQ(err, w, r, js)
}
diff --git a/schema/mssql/query_users.sql b/schema/mssql/query_users.sql
index eef6531a..bf3f9899 100644
--- a/schema/mssql/query_users.sql
+++ b/schema/mssql/query_users.sql
@@ -10,6 +10,7 @@ CREATE TABLE [users] (
[lastActiveAt] datetime not null,
[session] nvarchar (200) DEFAULT '' 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,
[avatar] nvarchar (100) DEFAULT '' not null,
[message] nvarchar (MAX) DEFAULT '' not null,
diff --git a/schema/mysql/query_users.sql b/schema/mysql/query_users.sql
index 7425f95a..a21a1d1f 100644
--- a/schema/mysql/query_users.sql
+++ b/schema/mysql/query_users.sql
@@ -10,6 +10,7 @@ CREATE TABLE `users` (
`lastActiveAt` datetime not null,
`session` varchar(200) DEFAULT '' 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,
`avatar` varchar(100) DEFAULT '' not null,
`message` text not null,
diff --git a/schema/pgsql/query_users.sql b/schema/pgsql/query_users.sql
index 9719b321..525983da 100644
--- a/schema/pgsql/query_users.sql
+++ b/schema/pgsql/query_users.sql
@@ -10,6 +10,7 @@ CREATE TABLE "users" (
`lastActiveAt` timestamp not null,
`session` varchar (200) DEFAULT '' 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,
`avatar` varchar (100) DEFAULT '' not null,
`message` text DEFAULT '' not null,
diff --git a/templates/account_menu.html b/templates/account_menu.html
index 2e45290f..243e45da 100644
--- a/templates/account_menu.html
+++ b/templates/account_menu.html
@@ -7,6 +7,7 @@