We made more progress with topic views on Cosora. We accidentally broke Tempra Conflux's topic view in the process and we're planning to fix it in the following commit.

Added the ForumPermsStore.
Fixed the creation dates of the posts.
Added a skippable version of the vhooks.
Social Groups now work again, although I'm planning to refactor them ;)
Added more Markdown tests and fixed a few Markdown bugs.
This commit is contained in:
Azareal 2017-11-02 02:52:21 +00:00
parent 0361310eb2
commit 6f45c62815
18 changed files with 521 additions and 348 deletions

View File

@ -49,6 +49,7 @@ func initDatabase() (err error) {
if err != nil { if err != nil {
return err return err
} }
fpstore = NewForumPermsStore()
log.Print("Loading the settings.") log.Print("Loading the settings.")
err = LoadSettings() err = LoadSettings()

View File

@ -21,8 +21,6 @@ var hooks = map[string][]func(interface{}) interface{}{
// Hooks with a variable number of arguments // Hooks with a variable number of arguments
var vhooks = map[string]func(...interface{}) interface{}{ var vhooks = map[string]func(...interface{}) interface{}{
"simple_forum_check_pre_perms": nil,
"forum_check_pre_perms": nil,
"intercept_build_widgets": nil, "intercept_build_widgets": nil,
"forum_trow_assign": nil, "forum_trow_assign": nil,
"topics_topic_row_assign": nil, "topics_topic_row_assign": nil,
@ -32,6 +30,14 @@ var vhooks = map[string]func(...interface{}) interface{}{
"topic_create_pre_loop": nil, "topic_create_pre_loop": nil,
} }
// Hooks with a variable number of arguments and return values for skipping the parent function and propagating an error upwards
var vhookSkippable = map[string]func(...interface{}) (bool, RouteError){
"simple_forum_check_pre_perms": nil,
"forum_check_pre_perms": nil,
}
//var vhookErrorable = map[string]func(...interface{}) (interface{}, RouteError){}
// Coming Soon: // Coming Soon:
type Message interface { type Message interface {
ID() int ID() int
@ -221,6 +227,9 @@ func (plugin *Plugin) AddHook(name string, handler interface{}) {
case func(...interface{}) interface{}: case func(...interface{}) interface{}:
vhooks[name] = h vhooks[name] = h
plugin.Hooks[name] = 0 plugin.Hooks[name] = 0
case func(...interface{}) (bool, RouteError):
vhookSkippable[name] = h
plugin.Hooks[name] = 0
default: default:
panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()? panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()?
} }
@ -258,6 +267,8 @@ func (plugin *Plugin) RemoveHook(name string, handler interface{}) {
preRenderHooks[name] = hook preRenderHooks[name] = hook
case func(...interface{}) interface{}: case func(...interface{}) interface{}:
delete(vhooks, name) delete(vhooks, name)
case func(...interface{}) (bool, RouteError):
delete(vhookSkippable, name)
default: default:
panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()? panic("I don't recognise this kind of handler!") // Should this be an error for the plugin instead of a panic()?
} }
@ -302,6 +313,10 @@ func runVhook(name string, data ...interface{}) interface{} {
return vhooks[name](data...) return vhooks[name](data...)
} }
func runVhookSkippable(name string, data ...interface{}) (bool, RouteError) {
return vhookSkippable[name](data...)
}
func runVhookNoreturn(name string, data ...interface{}) { func runVhookNoreturn(name string, data ...interface{}) {
_ = vhooks[name](data...) _ = vhooks[name](data...)
} }

19
forum_perms_store.go Normal file
View File

@ -0,0 +1,19 @@
package main
var fpstore *ForumPermsStore
type ForumPermsStore struct {
}
func NewForumPermsStore() *ForumPermsStore {
return &ForumPermsStore{}
}
func (fps *ForumPermsStore) Get(fid int, gid int) (fperms ForumPerms, err error) {
// TODO: Add a hook here and have plugin_socialgroups use it
group, err := gstore.Get(gid)
if err != nil {
return fperms, ErrNoRows
}
return group.Forums[fid], nil
}

View File

@ -532,8 +532,8 @@ func buildForumPermissions() error {
groups, err := gstore.GetAll() groups, err := gstore.GetAll()
if err != nil { if err != nil {
return err return err
} }
for _, group := range groups { for _, group := range groups {
if dev.DebugMode { if dev.DebugMode {
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(group.ID) + " - " + group.Name) log.Print("Adding the forum permissions for Group #" + strconv.Itoa(group.ID) + " - " + group.Name)

View File

@ -45,7 +45,7 @@ func deactivateMarkdown() {
// An adapter for the parser, so that the parser can call itself recursively. // An adapter for the parser, so that the parser can call itself recursively.
// This is less for the simple Markdown elements like bold and italics and more for the really complicated ones I plan on adding at some point. // This is less for the simple Markdown elements like bold and italics and more for the really complicated ones I plan on adding at some point.
func markdownParse(msg string) string { func markdownParse(msg string) string {
msg = strings.TrimSpace(_markdownParse(msg+" ", 0)) msg = strings.TrimSuffix(_markdownParse(msg+" ", 0), " ")
log.Print("final msg: ", msg) log.Print("final msg: ", msg)
return msg return msg
} }
@ -172,9 +172,9 @@ func _markdownParse(msg string, n int) string {
index++ index++
//log.Print("preskip index",index) //log.Print("preskip index: ", index)
//log.Print("preskip msg[index]",msg[index]) //log.Print("preskip msg[index]: ", msg[index])
//log.Print("preskip string(msg[index])",string(msg[index])) //log.Print("preskip string(msg[index]): ", string(msg[index]))
index = markdownSkipUntilAsterisk(msg, index) index = markdownSkipUntilAsterisk(msg, index)
if index >= len(msg) { if index >= len(msg) {
@ -222,8 +222,8 @@ func _markdownParse(msg string, n int) string {
sIndex++ sIndex++
} }
//log.Print("sIndex",sIndex) //log.Print("sIndex: ", sIndex)
//log.Print("lIndex",lIndex) //log.Print("lIndex: ", lIndex)
if lIndex <= sIndex { if lIndex <= sIndex {
//log.Print("unclosed markdown element @ lIndex <= sIndex") //log.Print("unclosed markdown element @ lIndex <= sIndex")
@ -241,19 +241,19 @@ func _markdownParse(msg string, n int) string {
break break
} }
//log.Print("final sIndex",sIndex) //log.Print("final sIndex: ", sIndex)
//log.Print("final lIndex",lIndex) //log.Print("final lIndex: ",lIndex)
//log.Print("final index",index) //log.Print("final index: ", index)
//log.Print("final msg[index]",msg[index]) //log.Print("final msg[index]: ", msg[index])
//log.Print("final string(msg[index])",string(msg[index])) //log.Print("final string(msg[index]): ", string(msg[index]))
//log.Print("final msg[sIndex]",msg[sIndex]) //log.Print("final msg[sIndex]: ", msg[sIndex])
//log.Print("final string(msg[sIndex])",string(msg[sIndex])) //log.Print("final string(msg[sIndex]): ", string(msg[sIndex]))
//log.Print("final msg[lIndex]",msg[lIndex]) //log.Print("final msg[lIndex]: ", msg[lIndex])
//log.Print("final string(msg[lIndex])",string(msg[lIndex])) //log.Print("final string(msg[lIndex]): ", string(msg[lIndex]))
//log.Print("[]byte(msg[:sIndex])",[]byte(msg[:sIndex])) //log.Print("[]byte(msg[:sIndex]): ", []byte(msg[:sIndex]))
//log.Print("[]byte(msg[:lIndex])",[]byte(msg[:lIndex])) //log.Print("[]byte(msg[:lIndex]): ", []byte(msg[:lIndex]))
outbytes = append(outbytes, msg[lastElement:startIndex]...) outbytes = append(outbytes, msg[lastElement:startIndex]...)
@ -278,13 +278,13 @@ func _markdownParse(msg string, n int) string {
index-- index--
case '\\': case '\\':
if (index + 1) < len(msg) { if (index + 1) < len(msg) {
if isMarkdownStartChar(msg[index+1]) && msg[index+1] != '\\' {
outbytes = append(outbytes, msg[lastElement:index]...) outbytes = append(outbytes, msg[lastElement:index]...)
index++ index++
lastElement = index lastElement = index
} }
}
//case '`': //case '`':
//case '_':
//case '~':
//case 10: // newline //case 10: // newline
} }
} }
@ -298,6 +298,10 @@ func _markdownParse(msg string, n int) string {
return string(outbytes) return string(outbytes)
} }
func isMarkdownStartChar(char byte) bool {
return char == '\\' || char == '~' || char == '_' || char == 10 || char == '`' || char == '*'
}
func markdownFindChar(data string, index int, char byte) bool { func markdownFindChar(data string, index int, char byte) bool {
for ; index < len(data); index++ { for ; index < len(data); index++ {
item := data[index] item := data[index]

View File

@ -561,7 +561,7 @@ func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} {
// TODO: Add privacy options // TODO: Add privacy options
// TODO: Add support for multiple boards and add per-board simplified permissions // TODO: Add support for multiple boards and add per-board simplified permissions
// TODO: Take isJs into account for routes which expect JSON responses // TODO: Take isJs into account for routes which expect JSON responses
func socialgroupsForumCheck(args ...interface{}) (skip interface{}) { func socialgroupsForumCheck(args ...interface{}) (skip bool, rerr RouteError) {
var r = args[1].(*http.Request) var r = args[1].(*http.Request)
var fid = args[3].(*int) var fid = args[3].(*int)
var forum = fstore.DirtyGet(*fid) var forum = fstore.DirtyGet(*fid)
@ -569,19 +569,14 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
if forum.ParentType == "socialgroup" { if forum.ParentType == "socialgroup" {
var err error var err error
var w = args[0].(http.ResponseWriter) var w = args[0].(http.ResponseWriter)
var success = args[4].(*bool)
sgItem, ok := r.Context().Value("socialgroups_current_group").(*SocialGroup) sgItem, ok := r.Context().Value("socialgroups_current_group").(*SocialGroup)
if !ok { if !ok {
sgItem, err = socialgroupsGetGroup(forum.ParentID) sgItem, err = socialgroupsGetGroup(forum.ParentID)
if err != nil { if err != nil {
InternalError(errors.New("Unable to find the parent group for a forum"), w, r) return true, InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
*success = false
return false
} }
if !sgItem.Active { if !sgItem.Active {
NotFound(w, r) return true, NotFound(w, r)
*success = false
return false
} }
r = r.WithContext(context.WithValue(r.Context(), "socialgroups_current_group", sgItem)) r = r.WithContext(context.WithValue(r.Context(), "socialgroups_current_group", sgItem))
} }
@ -600,16 +595,16 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
err = socialgroupsGetMemberStmt.QueryRow(sgItem.ID, user.ID).Scan(&rank, &posts, &joinedAt) err = socialgroupsGetMemberStmt.QueryRow(sgItem.ID, user.ID).Scan(&rank, &posts, &joinedAt)
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
*success = false return true, InternalError(err, w, r)
InternalError(err, w, r)
return false
} else if err != nil { } else if err != nil {
return true // TODO: Should we let admins / guests into public groups?
return true, LocalError("You're not part of this group!", w, r, *user)
} }
// TODO: Implement bans properly by adding the Local Ban API in the next commit // TODO: Implement bans properly by adding the Local Ban API in the next commit
// TODO: How does this even work? Refactor it along with the rest of this plugin!
if rank < 0 { if rank < 0 {
return true return true, LocalError("You've been banned from this group!", w, r, *user)
} }
// Basic permissions for members, more complicated permissions coming in the next commit! // Basic permissions for members, more complicated permissions coming in the next commit!
@ -622,10 +617,10 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
} else { } else {
overrideForumPerms(&user.Perms, true) overrideForumPerms(&user.Perms, true)
} }
return true return true, nil
} }
return false return false, nil
} }
// TODO: Override redirects? I don't think this is needed quite yet // TODO: Override redirects? I don't think this is needed quite yet

View File

@ -214,6 +214,8 @@ func TestMarkdownRender(t *testing.T) {
msgList = addMEPair(msgList, "**hi*", "*<i>hi</i>") msgList = addMEPair(msgList, "**hi*", "*<i>hi</i>")
msgList = addMEPair(msgList, "***hi***", "<b><i>hi</i></b>") msgList = addMEPair(msgList, "***hi***", "<b><i>hi</i></b>")
msgList = addMEPair(msgList, "***h***", "<b><i>h</i></b>") msgList = addMEPair(msgList, "***h***", "<b><i>h</i></b>")
msgList = addMEPair(msgList, "\\***h**\\*", "*<b>h</b>*")
msgList = addMEPair(msgList, "\\*\\**h*\\*\\*", "**<i>h</i>**")
msgList = addMEPair(msgList, "\\*hi\\*", "*hi*") msgList = addMEPair(msgList, "\\*hi\\*", "*hi*")
msgList = addMEPair(msgList, "d\\*hi\\*", "d*hi*") msgList = addMEPair(msgList, "d\\*hi\\*", "d*hi*")
msgList = addMEPair(msgList, "\\*hi\\*d", "*hi*d") msgList = addMEPair(msgList, "\\*hi\\*d", "*hi*d")
@ -225,8 +227,10 @@ func TestMarkdownRender(t *testing.T) {
msgList = addMEPair(msgList, "\\\\\\d", "\\\\\\d") msgList = addMEPair(msgList, "\\\\\\d", "\\\\\\d")
msgList = addMEPair(msgList, "d\\", "d\\") msgList = addMEPair(msgList, "d\\", "d\\")
msgList = addMEPair(msgList, "\\d\\", "\\d\\") msgList = addMEPair(msgList, "\\d\\", "\\d\\")
msgList = addMEPair(msgList, "*_hi_*", "<i><u>hi</u></i>")
msgList = addMEPair(msgList, "*~hi~*", "<i><s>hi</s></i>") msgList = addMEPair(msgList, "*~hi~*", "<i><s>hi</s></i>")
msgList = addMEPair(msgList, "~*hi*~", "<s><i>hi</i></s>") msgList = addMEPair(msgList, "~*hi*~", "<s><i>hi</i></s>")
msgList = addMEPair(msgList, "~ *hi* ~", "<s> <i>hi</i> </s>")
msgList = addMEPair(msgList, "_~hi~_", "<u><s>hi</s></u>") msgList = addMEPair(msgList, "_~hi~_", "<u><s>hi</s></u>")
msgList = addMEPair(msgList, "***~hi~***", "<b><i><s>hi</s></i></b>") msgList = addMEPair(msgList, "***~hi~***", "<b><i><s>hi</s></i></b>")
msgList = addMEPair(msgList, "**", "**") msgList = addMEPair(msgList, "**", "**")
@ -255,4 +259,37 @@ func TestMarkdownRender(t *testing.T) {
t.Error("Expected:", item.Expects) t.Error("Expected:", item.Expects)
} }
} }
for _, item := range msgList {
res = markdownParse("\n" + item.Msg)
if res != "\n"+item.Expects {
t.Error("Testing string '\n" + item.Msg + "'")
t.Error("Bad output:", "'"+res+"'")
//t.Error("Ouput in bytes:", []byte(res))
t.Error("Expected:", "\n"+item.Expects)
}
}
for _, item := range msgList {
res = markdownParse("\t" + item.Msg)
if res != "\t"+item.Expects {
t.Error("Testing string '\t" + item.Msg + "'")
t.Error("Bad output:", "'"+res+"'")
//t.Error("Ouput in bytes:", []byte(res))
t.Error("Expected:", "\t"+item.Expects)
}
}
for _, item := range msgList {
res = markdownParse("d" + item.Msg)
if res != "d"+item.Expects {
t.Error("Testing string 'd" + item.Msg + "'")
t.Error("Bad output:", "'"+res+"'")
//t.Error("Ouput in bytes:", []byte(res))
t.Error("Expected:", "d"+item.Expects)
}
}
// TODO: Write suffix tests and double string tests
// TODO: Write similar prefix, suffix, and double string tests for plugin_bbcode. Ditto for the outer parser along with suitable tests for that like making sure the URL parser and media embedder works.
} }

View File

@ -6,7 +6,10 @@
*/ */
package main package main
import "errors" import (
"errors"
"time"
)
// ? - Should we add a reply store to centralise all the reply logic? Would this cover profile replies too or would that be separate? // ? - Should we add a reply store to centralise all the reply logic? Would this cover profile replies too or would that be separate?
var rstore ReplyStore var rstore ReplyStore
@ -21,7 +24,8 @@ type ReplyUser struct {
UserLink string UserLink string
CreatedByName string CreatedByName string
Group int Group int
CreatedAt string CreatedAt time.Time
RelativeCreatedAt string
LastEdit int LastEdit int
LastEditBy int LastEditBy int
Avatar string Avatar string
@ -45,7 +49,8 @@ type Reply struct {
Content string Content string
CreatedBy int CreatedBy int
Group int Group int
CreatedAt string CreatedAt time.Time
RelativeCreatedAt string
LastEdit int LastEdit int
LastEditBy int LastEditBy int
ContentLines int ContentLines int

View File

@ -533,22 +533,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user User) RouteError
if postGroup.IsMod || postGroup.IsAdmin { if postGroup.IsMod || postGroup.IsAdmin {
topic.ClassName = config.StaffCSS topic.ClassName = config.StaffCSS
} }
topic.RelativeCreatedAt = relativeTime(topic.CreatedAt)
/*if headerVars.Settings["url_tags"] == false {
topic.URLName = ""
} else {
topic.URL, ok = external_sites[topic.URLPrefix]
if !ok {
topic.URL = topic.URLName
} else {
topic.URL = topic.URL + topic.URLName
}
}*/
topic.CreatedAt, err = relativeTimeFromString(topic.CreatedAt)
if err != nil {
topic.CreatedAt = ""
}
// TODO: Make a function for this? Build a more sophisticated noavatar handling system? // TODO: Make a function for this? Build a more sophisticated noavatar handling system?
if topic.Avatar != "" { if topic.Avatar != "" {
@ -604,7 +589,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user User) RouteError
replyItem.ClassName = "" replyItem.ClassName = ""
} }
// TODO: Make a function for this? Build a more sophisticated noavatar handling system? // TODO: Make a function for this? Build a more sophisticated noavatar handling system? Do bulk user loads and let the UserStore initialise this?
if replyItem.Avatar != "" { if replyItem.Avatar != "" {
if replyItem.Avatar[0] == '.' { if replyItem.Avatar[0] == '.' {
replyItem.Avatar = "/uploads/avatar_" + strconv.Itoa(replyItem.CreatedBy) + replyItem.Avatar replyItem.Avatar = "/uploads/avatar_" + strconv.Itoa(replyItem.CreatedBy) + replyItem.Avatar
@ -614,22 +599,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user User) RouteError
} }
replyItem.Tag = postGroup.Tag replyItem.Tag = postGroup.Tag
replyItem.RelativeCreatedAt = relativeTime(replyItem.CreatedAt)
/*if headerVars.Settings["url_tags"] == false {
replyItem.URLName = ""
} else {
replyItem.URL, ok = external_sites[replyItem.URLPrefix]
if !ok {
replyItem.URL = replyItem.URLName
} else {
replyItem.URL = replyItem.URL + replyItem.URLName
}
}*/
replyItem.CreatedAt, err = relativeTimeFromString(replyItem.CreatedAt)
if err != nil {
replyItem.CreatedAt = ""
}
// We really shouldn't have inline HTML, we should do something about this... // We really shouldn't have inline HTML, we should do something about this...
if replyItem.ActionType != "" { if replyItem.ActionType != "" {
@ -683,7 +653,8 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user User) RouteError
} }
var err error var err error
var replyContent, replyCreatedByName, replyCreatedAt, replyAvatar, replyTag, replyClassName string var replyCreatedAt time.Time
var replyContent, replyCreatedByName, replyRelativeCreatedAt, replyAvatar, replyTag, replyClassName string
var rid, replyCreatedBy, replyLastEdit, replyLastEditBy, replyLines, replyGroup int var rid, replyCreatedBy, replyLastEdit, replyLastEditBy, replyLines, replyGroup int
var replyList []ReplyUser var replyList []ReplyUser
@ -736,6 +707,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user User) RouteError
} else { } else {
replyClassName = "" replyClassName = ""
} }
if replyAvatar != "" { if replyAvatar != "" {
if replyAvatar[0] == '.' { if replyAvatar[0] == '.' {
replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar
@ -754,10 +726,11 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user User) RouteError
replyLiked := false replyLiked := false
replyLikeCount := 0 replyLikeCount := 0
replyRelativeCreatedAt = relativeTime(replyCreatedAt)
// TODO: Add a hook here // TODO: Add a hook here
replyList = append(replyList, ReplyUser{rid, puser.ID, replyContent, parseMessage(replyContent, 0, ""), replyCreatedBy, buildProfileURL(nameToSlug(replyCreatedByName), replyCreatedBy), replyCreatedByName, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, replyAvatar, replyClassName, replyLines, replyTag, "", "", "", 0, "", replyLiked, replyLikeCount, "", ""}) replyList = append(replyList, ReplyUser{rid, puser.ID, replyContent, parseMessage(replyContent, 0, ""), replyCreatedBy, buildProfileURL(nameToSlug(replyCreatedByName), replyCreatedBy), replyCreatedByName, replyGroup, replyCreatedAt, replyRelativeCreatedAt, replyLastEdit, replyLastEditBy, replyAvatar, replyClassName, replyLines, replyTag, "", "", "", 0, "", replyLiked, replyLikeCount, "", ""})
} }
err = rows.Err() err = rows.Err()
if err != nil { if err != nil {

View File

@ -59,51 +59,51 @@ func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fi
} }
// Is there a better way of doing the skip AND the success flag on this hook like multiple returns? // Is there a better way of doing the skip AND the success flag on this hook like multiple returns?
if vhooks["simple_forum_check_pre_perms"] != nil { if vhookSkippable["simple_forum_check_pre_perms"] != nil {
if runVhook("simple_forum_check_pre_perms", w, r, user, &fid, &rerr, &headerLite).(bool) { var skip bool
skip, rerr = runVhookSkippable("simple_forum_check_pre_perms", w, r, user, &fid, &headerLite)
if skip {
return headerLite, rerr return headerLite, rerr
} }
} }
group, err := gstore.Get(user.Group) fperms, err := fpstore.Get(fid, user.Group)
if err != nil { if err != nil {
// TODO: Refactor this // TODO: Refactor this
log.Printf("Group #%d doesn't exist despite being used by User #%d", user.Group, user.ID) log.Printf("Unable to get the forum perms for Group #%d for User #%d", user.Group, user.ID)
return nil, PreError("Something weird happened", w, r) return nil, PreError("Something weird happened", w, r)
} }
fperms := group.Forums[fid]
cascadeForumPerms(fperms, user) cascadeForumPerms(fperms, user)
return headerLite, nil return headerLite, nil
} }
func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, ferr RouteError) { func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, rerr RouteError) {
headerVars, ferr = UserCheck(w, r, user) headerVars, rerr = UserCheck(w, r, user)
if ferr != nil { if rerr != nil {
return headerVars, ferr return headerVars, rerr
} }
if !fstore.Exists(fid) { if !fstore.Exists(fid) {
return headerVars, NotFound(w, r) return headerVars, NotFound(w, r)
} }
if vhooks["forum_check_pre_perms"] != nil { if vhookSkippable["forum_check_pre_perms"] != nil {
if runVhook("forum_check_pre_perms", w, r, user, &fid, &ferr, &headerVars).(bool) { var skip bool
return headerVars, ferr skip, rerr = runVhookSkippable("forum_check_pre_perms", w, r, user, &fid, &headerVars)
if skip {
return headerVars, rerr
} }
} }
group, err := gstore.Get(user.Group) fperms, err := fpstore.Get(fid, user.Group)
if err != nil { if err != nil {
// TODO: Refactor this // TODO: Refactor this
log.Printf("Group #%d doesn't exist despite being used by User #%d", user.Group, user.ID) log.Printf("Unable to get the forum perms for Group #%d for User #%d", user.Group, user.ID)
return headerVars, PreError("Something weird happened", w, r) return nil, PreError("Something weird happened", w, r)
} }
fperms := group.Forums[fid]
//log.Printf("user.Perms: %+v\n", user.Perms) //log.Printf("user.Perms: %+v\n", user.Perms)
//log.Printf("fperms: %+v\n", fperms) //log.Printf("fperms: %+v\n", fperms)
cascadeForumPerms(fperms, user) cascadeForumPerms(fperms, user)
return headerVars, ferr return headerVars, rerr
} }
// TODO: Put this on the user instance? Do we really want forum specific logic in there? Maybe, a method which spits a new pointer with the same contents as user? // TODO: Put this on the user instance? Do we really want forum specific logic in there? Maybe, a method which spits a new pointer with the same contents as user?

View File

@ -92,9 +92,10 @@ func compileTemplates() error {
log.Print("Compiling the templates") log.Print("Compiling the templates")
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, "Date", time.Now(), "Date", 0, "", "127.0.0.1", 0, 1, "classname", "weird-data", buildProfileURL("fake-user", 62), "Fake User", config.DefaultGroup, "", 0, "", "", "", "", "", 58, false} var now = time.Now()
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, relativeTime(now), now, relativeTime(now), 0, "", "127.0.0.1", 0, 1, "classname", "weird-data", buildProfileURL("fake-user", 62), "Fake User", config.DefaultGroup, "", 0, "", "", "", "", "", 58, false}
var replyList []ReplyUser var replyList []ReplyUser
replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", config.DefaultGroup, "", 0, 0, "", "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, "", ""}) replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", config.DefaultGroup, now, relativeTime(now), 0, 0, "", "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, "", ""})
var varList = make(map[string]VarItem) var varList = make(map[string]VarItem)
tpage := TopicPage{"Title", user, headerVars, replyList, topic, 1, 1} tpage := TopicPage{"Title", user, headerVars, replyList, topic, 1, 1}

View File

@ -351,7 +351,7 @@ var topic_alt_29 = []byte(`</textarea>
<div class="button_container"> <div class="button_container">
`) `)
var topic_alt_30 = []byte(`<a href="/topic/like/submit/`) var topic_alt_30 = []byte(`<a href="/topic/like/submit/`)
var topic_alt_31 = []byte(`" class="action_button">+1</a>`) var topic_alt_31 = []byte(`" class="action_button like_item">+1</a>`)
var topic_alt_32 = []byte(`<a href="/topic/edit/`) var topic_alt_32 = []byte(`<a href="/topic/edit/`)
var topic_alt_33 = []byte(`" class="action_button open_edit">Edit</a>`) var topic_alt_33 = []byte(`" class="action_button open_edit">Edit</a>`)
var topic_alt_34 = []byte(`<a href="/topic/delete/submit/`) var topic_alt_34 = []byte(`<a href="/topic/delete/submit/`)
@ -369,106 +369,129 @@ var topic_alt_44 = []byte(`
var topic_alt_45 = []byte(`?session=`) var topic_alt_45 = []byte(`?session=`)
var topic_alt_46 = []byte(`&type=topic" class="action_button report_item">Report</a> var topic_alt_46 = []byte(`&type=topic" class="action_button report_item">Report</a>
`) `)
var topic_alt_47 = []byte(`<a class="action_button action_button_right like_count hide_on_micro">`) var topic_alt_47 = []byte(`
var topic_alt_48 = []byte(` up</a>`) <div class="action_button_right">
var topic_alt_49 = []byte(`
<a class="action_button action_button_right created_at hide_on_mobile">`)
var topic_alt_50 = []byte(`</a>
`) `)
var topic_alt_51 = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`) var topic_alt_48 = []byte(`<a class="action_button like_count hide_on_micro">`)
var topic_alt_52 = []byte(`</a>`) var topic_alt_49 = []byte(`</a>`)
var topic_alt_53 = []byte(` var topic_alt_50 = []byte(`
<a class="action_button created_at hide_on_mobile">`)
var topic_alt_51 = []byte(`</a>
`)
var topic_alt_52 = []byte(`<a href="#" title="IP Address" class="action_button ip_item hide_on_mobile">`)
var topic_alt_53 = []byte(`</a>`)
var topic_alt_54 = []byte(`
</div>
</div> </div>
</div><div style="clear:both;"></div> </div><div style="clear:both;"></div>
</article> </article>
`) `)
var topic_alt_54 = []byte(` var topic_alt_55 = []byte(`
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item `) <article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item `)
var topic_alt_55 = []byte(`action_item`) var topic_alt_56 = []byte(`action_item`)
var topic_alt_56 = []byte(`"> var topic_alt_57 = []byte(`">
<div class="userinfo" aria-label="The information on the poster"> <div class="userinfo" aria-label="The information on the poster">
<div class="avatar_item" style="background-image: url(`) <div class="avatar_item" style="background-image: url(`)
var topic_alt_57 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div> var topic_alt_58 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="`) <a href="`)
var topic_alt_58 = []byte(`" class="the_name" rel="author">`) var topic_alt_59 = []byte(`" class="the_name" rel="author">`)
var topic_alt_59 = []byte(`</a> var topic_alt_60 = []byte(`</a>
`) `)
var topic_alt_60 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`) var topic_alt_61 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_61 = []byte(`</div><div class="tag_post"></div></div>`) var topic_alt_62 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_62 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `) var topic_alt_63 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_63 = []byte(`</div><div class="tag_post"></div></div>`) var topic_alt_64 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_64 = []byte(` var topic_alt_65 = []byte(`
</div> </div>
<div class="content_container" `) <div class="content_container" `)
var topic_alt_65 = []byte(`style="margin-left: 0px;"`) var topic_alt_66 = []byte(`style="margin-left: 0px;"`)
var topic_alt_66 = []byte(`> var topic_alt_67 = []byte(`>
`) `)
var topic_alt_67 = []byte(` var topic_alt_68 = []byte(`
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`) <span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
var topic_alt_68 = []byte(`</span>
<span itemprop="text">`)
var topic_alt_69 = []byte(`</span> var topic_alt_69 = []byte(`</span>
<span itemprop="text">`)
var topic_alt_70 = []byte(`</span>
`) `)
var topic_alt_70 = []byte(` var topic_alt_71 = []byte(`
<div class="editable_block user_content" itemprop="text">`) <div class="editable_block user_content" itemprop="text">`)
var topic_alt_71 = []byte(`</div> var topic_alt_72 = []byte(`</div>
<div class="button_container"> <div class="button_container">
`) `)
var topic_alt_72 = []byte(`<a href="/reply/like/submit/`) var topic_alt_73 = []byte(`<a href="/reply/like/submit/`)
var topic_alt_73 = []byte(`" class="action_button">+1</a>`) var topic_alt_74 = []byte(`" class="action_button like_item">+1</a>`)
var topic_alt_74 = []byte(`<a href="/reply/edit/submit/`) var topic_alt_75 = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_75 = []byte(`" class="action_button edit_item">Edit</a>`) var topic_alt_76 = []byte(`" class="action_button edit_item">Edit</a>`)
var topic_alt_76 = []byte(`<a href="/reply/delete/submit/`) var topic_alt_77 = []byte(`<a href="/reply/delete/submit/`)
var topic_alt_77 = []byte(`" class="action_button delete_item">Delete</a>`) var topic_alt_78 = []byte(`" class="action_button delete_item">Delete</a>`)
var topic_alt_78 = []byte(` var topic_alt_79 = []byte(`
<a href="/report/submit/`) <a href="/report/submit/`)
var topic_alt_79 = []byte(`?session=`) var topic_alt_80 = []byte(`?session=`)
var topic_alt_80 = []byte(`&type=reply" class="action_button report_item">Report</a> var topic_alt_81 = []byte(`&type=reply" class="action_button report_item">Report</a>
`) `)
var topic_alt_81 = []byte(`<a class="action_button action_button_right like_count hide_on_micro">`) var topic_alt_82 = []byte(`
var topic_alt_82 = []byte(` up</a>`) <div class="action_button_right">
var topic_alt_83 = []byte(`
<a class="action_button action_button_right created_at hide_on_mobile">`)
var topic_alt_84 = []byte(`</a>
`) `)
var topic_alt_85 = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`) var topic_alt_83 = []byte(`<a class="action_button like_count hide_on_micro">`)
var topic_alt_86 = []byte(`</a>`) var topic_alt_84 = []byte(`</a>`)
var topic_alt_87 = []byte(` var topic_alt_85 = []byte(`
<a class="action_button created_at hide_on_mobile">`)
var topic_alt_86 = []byte(`</a>
`)
var topic_alt_87 = []byte(`<a href="#" title="IP Address" class="action_button ip_item hide_on_mobile">`)
var topic_alt_88 = []byte(`</a>`)
var topic_alt_89 = []byte(`
</div>
</div> </div>
`) `)
var topic_alt_88 = []byte(` var topic_alt_90 = []byte(`
</div> </div>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</article> </article>
`) `)
var topic_alt_89 = []byte(`</div> var topic_alt_91 = []byte(`</div>
`) `)
var topic_alt_90 = []byte(` var topic_alt_92 = []byte(`
<div class="rowblock topic_reply_container">
<div class="userinfo" aria-label="The information on the poster">
<div class="avatar_item" style="background-image: url(`)
var topic_alt_93 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="`)
var topic_alt_94 = []byte(`" class="the_name" rel="author">`)
var topic_alt_95 = []byte(`</a>
`)
var topic_alt_96 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_97 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_98 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_99 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_100 = []byte(`
</div>
<div class="rowblock topic_reply_form quick_create_form"> <div class="rowblock topic_reply_form quick_create_form">
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form> <form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
<input form="reply_form" name="tid" value='`) <input form="reply_form" name="tid" value='`)
var topic_alt_91 = []byte(`' type="hidden" /> var topic_alt_101 = []byte(`' type="hidden" />
<div class="formrow real_first_child"> <div class="formrow real_first_child">
<div class="formitem"> <div class="formitem">
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea> <textarea id="input_content" form="reply_form" name="reply-content" placeholder="What do you think?" required></textarea>
</div> </div>
</div> </div>
<div class="formrow quick_button_row"> <div class="formrow quick_button_row">
<div class="formitem"> <div class="formitem">
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button> <button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
`) `)
var topic_alt_92 = []byte(` var topic_alt_102 = []byte(`
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" /> <input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">Add File</label> <label for="upload_files" class="formbutton add_file_button">Add File</label>
<div id="upload_file_dock"></div>`) <div id="upload_file_dock"></div>`)
var topic_alt_93 = []byte(` var topic_alt_103 = []byte(`
</div>
</div> </div>
</div> </div>
</div> </div>
`) `)
var topic_alt_94 = []byte(` var topic_alt_104 = []byte(`
</main> </main>

View File

@ -3,8 +3,8 @@
// Code generated by Gosora. More below: // Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "net/http"
import "strconv" import "strconv"
import "net/http"
// nolint // nolint
func init() { func init() {
@ -182,108 +182,126 @@ w.Write(topic_alt_45)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_46) w.Write(topic_alt_46)
} }
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_47) w.Write(topic_alt_47)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount))) if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_48) w.Write(topic_alt_48)
} w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_49) w.Write(topic_alt_49)
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedAt))
w.Write(topic_alt_50)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_51)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_52)
} }
w.Write(topic_alt_50)
w.Write([]byte(tmpl_topic_alt_vars.Topic.RelativeCreatedAt))
w.Write(topic_alt_51)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_52)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_53) w.Write(topic_alt_53)
}
w.Write(topic_alt_54)
if len(tmpl_topic_alt_vars.ItemList) != 0 { if len(tmpl_topic_alt_vars.ItemList) != 0 {
for _, item := range tmpl_topic_alt_vars.ItemList { for _, item := range tmpl_topic_alt_vars.ItemList {
w.Write(topic_alt_54)
if item.ActionType != "" {
w.Write(topic_alt_55) w.Write(topic_alt_55)
} if item.ActionType != "" {
w.Write(topic_alt_56) w.Write(topic_alt_56)
w.Write([]byte(item.Avatar)) }
w.Write(topic_alt_57) w.Write(topic_alt_57)
w.Write([]byte(item.UserLink)) w.Write([]byte(item.Avatar))
w.Write(topic_alt_58) w.Write(topic_alt_58)
w.Write([]byte(item.CreatedByName)) w.Write([]byte(item.UserLink))
w.Write(topic_alt_59) w.Write(topic_alt_59)
if item.Tag != "" { w.Write([]byte(item.CreatedByName))
w.Write(topic_alt_60) w.Write(topic_alt_60)
w.Write([]byte(item.Tag)) if item.Tag != "" {
w.Write(topic_alt_61) w.Write(topic_alt_61)
} else { w.Write([]byte(item.Tag))
w.Write(topic_alt_62) w.Write(topic_alt_62)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_63)
}
w.Write(topic_alt_64)
if item.ActionType != "" {
w.Write(topic_alt_65)
}
w.Write(topic_alt_66)
if item.ActionType != "" {
w.Write(topic_alt_67)
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_68)
w.Write([]byte(item.ActionType))
w.Write(topic_alt_69)
} else { } else {
w.Write(topic_alt_63)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_64)
}
w.Write(topic_alt_65)
if item.ActionType != "" {
w.Write(topic_alt_66)
}
w.Write(topic_alt_67)
if item.ActionType != "" {
w.Write(topic_alt_68)
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_69)
w.Write([]byte(item.ActionType))
w.Write(topic_alt_70) w.Write(topic_alt_70)
w.Write([]byte(item.ContentHtml)) } else {
w.Write(topic_alt_71) w.Write(topic_alt_71)
w.Write([]byte(item.ContentHtml))
w.Write(topic_alt_72)
if tmpl_topic_alt_vars.CurrentUser.Loggedin { if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem { if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_72)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_73) w.Write(topic_alt_73)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_74)
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply { if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_74)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_75) w.Write(topic_alt_75)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_76)
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply { if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_76)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_77) w.Write(topic_alt_77)
}
w.Write(topic_alt_78)
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_78)
}
w.Write(topic_alt_79) w.Write(topic_alt_79)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_80) w.Write(topic_alt_80)
} w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
if item.LikeCount > 0 {
w.Write(topic_alt_81) w.Write(topic_alt_81)
w.Write([]byte(strconv.Itoa(item.LikeCount))) }
w.Write(topic_alt_82) w.Write(topic_alt_82)
} if item.LikeCount > 0 {
w.Write(topic_alt_83) w.Write(topic_alt_83)
w.Write([]byte(item.CreatedAt)) w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_alt_84) w.Write(topic_alt_84)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { }
w.Write(topic_alt_85) w.Write(topic_alt_85)
w.Write([]byte(item.IPAddress)) w.Write([]byte(item.RelativeCreatedAt))
w.Write(topic_alt_86) w.Write(topic_alt_86)
} if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_87) w.Write(topic_alt_87)
} w.Write([]byte(item.IPAddress))
w.Write(topic_alt_88) w.Write(topic_alt_88)
} }
}
w.Write(topic_alt_89) w.Write(topic_alt_89)
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply { }
w.Write(topic_alt_90) w.Write(topic_alt_90)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) }
}
w.Write(topic_alt_91) w.Write(topic_alt_91)
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles { if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_alt_92) w.Write(topic_alt_92)
} w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar))
w.Write(topic_alt_93) w.Write(topic_alt_93)
} w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
w.Write(topic_alt_94) w.Write(topic_alt_94)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name))
w.Write(topic_alt_95)
if tmpl_topic_alt_vars.CurrentUser.Tag != "" {
w.Write(topic_alt_96)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag))
w.Write(topic_alt_97)
} else {
w.Write(topic_alt_98)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.CurrentUser.Level)))
w.Write(topic_alt_99)
}
w.Write(topic_alt_100)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_101)
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles {
w.Write(topic_alt_102)
}
w.Write(topic_alt_103)
}
w.Write(topic_alt_104)
w.Write(footer_0) w.Write(footer_0)
if len(tmpl_topic_alt_vars.Header.Themes) != 0 { if len(tmpl_topic_alt_vars.Header.Themes) != 0 {
for _, item := range tmpl_topic_alt_vars.Header.Themes { for _, item := range tmpl_topic_alt_vars.Header.Themes {

View File

@ -34,7 +34,7 @@
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
<div class="button_container"> <div class="button_container">
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button">+1</a>{{end}} {{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button like_item">+1</a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit">Edit</a>{{end}} {{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit">Edit</a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}} {{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}}
{{if .CurrentUser.Perms.CloseTopic}} {{if .CurrentUser.Perms.CloseTopic}}
@ -43,9 +43,11 @@
{{if .Topic.Sticky}}<a href='/topic/unstick/submit/{{.Topic.ID}}' class="action_button">Unpin</a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="action_button">Pin</a>{{end}}{{end}} {{if .Topic.Sticky}}<a href='/topic/unstick/submit/{{.Topic.ID}}' class="action_button">Unpin</a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="action_button">Pin</a>{{end}}{{end}}
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item">Report</a> <a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item">Report</a>
{{end}} {{end}}
{{if .Topic.LikeCount}}<a class="action_button action_button_right like_count hide_on_micro">{{.Topic.LikeCount}} up</a>{{end}} <div class="action_button_right">
<a class="action_button action_button_right created_at hide_on_mobile">{{.Topic.CreatedAt}}</a> {{if .Topic.LikeCount}}<a class="action_button like_count hide_on_micro">{{.Topic.LikeCount}}</a>{{end}}
{{if .CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.Topic.IPAddress}}</a>{{end}} <a class="action_button created_at hide_on_mobile">{{.Topic.RelativeCreatedAt}}</a>
{{if .CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button ip_item hide_on_mobile">{{.Topic.IPAddress}}</a>{{end}}
</div>
</div> </div>
</div><div style="clear:both;"></div> </div><div style="clear:both;"></div>
</article> </article>
@ -66,14 +68,16 @@
<div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div> <div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div>
<div class="button_container"> <div class="button_container">
{{if $.CurrentUser.Loggedin}} {{if $.CurrentUser.Loggedin}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button">+1</a>{{end}} {{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button like_item">+1</a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item">Edit</a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item">Edit</a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item">Delete</a>{{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item">Delete</a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item">Report</a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item">Report</a>
{{end}} {{end}}
{{if .LikeCount}}<a class="action_button action_button_right like_count hide_on_micro">{{.LikeCount}} up</a>{{end}} <div class="action_button_right">
<a class="action_button action_button_right created_at hide_on_mobile">{{.CreatedAt}}</a> {{if .LikeCount}}<a class="action_button like_count hide_on_micro">{{.LikeCount}}</a>{{end}}
{{if $.CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.IPAddress}}</a>{{end}} <a class="action_button created_at hide_on_mobile">{{.RelativeCreatedAt}}</a>
{{if $.CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button ip_item hide_on_mobile">{{.IPAddress}}</a>{{end}}
</div>
</div> </div>
{{end}} {{end}}
</div> </div>
@ -82,12 +86,18 @@
{{end}}</div> {{end}}</div>
{{if .CurrentUser.Perms.CreateReply}} {{if .CurrentUser.Perms.CreateReply}}
<div class="rowblock topic_reply_container">
<div class="userinfo" aria-label="The information on the poster">
<div class="avatar_item" style="background-image: url({{.CurrentUser.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a>
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level {{.CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
<div class="rowblock topic_reply_form quick_create_form"> <div class="rowblock topic_reply_form quick_create_form">
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form> <form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
<input form="reply_form" name="tid" value='{{.Topic.ID}}' type="hidden" /> <input form="reply_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
<div class="formrow real_first_child"> <div class="formrow real_first_child">
<div class="formitem"> <div class="formitem">
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea> <textarea id="input_content" form="reply_form" name="reply-content" placeholder="What do you think?" required></textarea>
</div> </div>
</div> </div>
<div class="formrow quick_button_row"> <div class="formrow quick_button_row">
@ -100,6 +110,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
{{end}} {{end}}
</main> </main>

View File

@ -8,8 +8,10 @@
--primary-link-color: hsl(0,0%,40%); --primary-link-color: hsl(0,0%,40%);
--primary-text-color: hsl(0,0%,20%); --primary-text-color: hsl(0,0%,20%);
--lightened-primary-text-color: hsl(0,0%,30%); --lightened-primary-text-color: hsl(0,0%,30%);
--extra-lightened-primary-text-color: hsl(0,0%,40%);
--inverse-primary-text-color: white; --inverse-primary-text-color: white;
--light-text-color: hsl(0,0%,55%); --light-text-color: hsl(0,0%,55%);
--lighter-text-color: hsl(0,0%,65%);
} }
* { * {
@ -402,6 +404,25 @@ select, input, textarea {
color: hsl(0,0%,30%); color: hsl(0,0%,30%);
} }
.topic_reply_container {
display: flex;
border: 0;
}
.topic_reply_form {
margin: 0px;
width: 100%;
}
.topic_reply_form .trumbowyg-button-pane:after {
display: none;
}
.topic_reply_form .trumbowyg-editor {
border-left: none;
border-right: none;
}
.topic_reply_form .quick_button_row {
margin-bottom: 7px;
}
#prevFloat, #nextFloat { #prevFloat, #nextFloat {
display: none; display: none;
} }
@ -600,33 +621,67 @@ select, input, textarea {
} }
.userinfo { .userinfo {
margin-right: 16px; margin-right: 16px;
padding: 18px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-left: 24px; padding-top: 30px;
padding-right: 24px; padding-left: 42px;
padding-bottom: 12px; padding-right: 42px;
padding-bottom: 18px;
height: min-content;
/*overflow: hidden;
text-overflow: ellipsis;*/
} }
.content_container { .content_container {
width: 100%; width: 100%;
padding: 16px; padding: 17px;
display: flex;
flex-direction: column;
} }
.avatar_item { .avatar_item {
border-radius: 50px; border-radius: 50px;
width: 90px; width: 84px;
height: 90px; height: 84px;
margin-bottom: 5px; margin-bottom: 12px;
background-size: 128px; background-size: 120px;
}
.the_name, .userinfo .tag_block {
margin-left: auto;
margin-right: auto;
} }
.the_name { .the_name {
font-size: 19px; font-size: 18px;
color: var(--lightened-primary-text-color); color: var(--lightened-primary-text-color);
margin-left: auto;
margin-right: auto;
} }
.userinfo .tag_block { .userinfo .tag_block {
color: var(--extra-lightened-primary-text-color);
}
.button_container {
margin-top: auto;
display: flex;
}
.action_button {
margin-right: 5px;
color: var(--light-text-color);
font-size: 14px;
display: inline-block;
}
.action_button_right {
display: inline-flex;
margin-left: auto; margin-left: auto;
margin-right: auto; }
.like_count:after {
content: " likes";
margin-right: 6px;
}
.created_at:before, .ip_item:before {
border-left: 1px solid var(--element-border-color);
content: "";
margin-right: 10px;
margin-top: 1px;
margin-bottom: 1px;
}
.created_at {
margin-right: 10px;
} }
@media(max-width: 670px) { @media(max-width: 670px) {

View File

@ -652,6 +652,9 @@ button.username {
padding-right: 7px; padding-right: 7px;
} }
.action_button_right { .action_button_right {
display: flex;
}
.action_button_right .action_button {
border-left: solid 1px #eaeaea; border-left: solid 1px #eaeaea;
border-right: none; border-right: none;
} }
@ -660,10 +663,21 @@ button.username {
margin-right: auto; margin-right: auto;
} }
.like_label:before { content: "😀"; } .like_label:before {
.edit_label:before { content: "🖊️"; } content: "😀";
.trash_label:before { content: "🗑️"; } }
.flag_label:before { content: "🚩"; } .like_count:after {
content: " up";
}
.edit_label:before {
content: "🖊️";
}
.trash_label:before {
content: "🗑️";
}
.flag_label:before {
content: "🚩";
}
.mod_button { .mod_button {
margin-right: 4px; margin-right: 4px;

View File

@ -24,7 +24,8 @@ type Topic struct {
CreatedBy int CreatedBy int
IsClosed bool IsClosed bool
Sticky bool Sticky bool
CreatedAt string CreatedAt time.Time
RelativeCreatedAt string
LastReplyAt time.Time LastReplyAt time.Time
RelativeLastReplyAt string RelativeLastReplyAt string
//LastReplyBy int //LastReplyBy int
@ -45,7 +46,8 @@ type TopicUser struct {
CreatedBy int CreatedBy int
IsClosed bool IsClosed bool
Sticky bool Sticky bool
CreatedAt string CreatedAt time.Time
RelativeCreatedAt string
LastReplyAt time.Time LastReplyAt time.Time
RelativeLastReplyAt string RelativeLastReplyAt string
//LastReplyBy int //LastReplyBy int