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:
parent
0361310eb2
commit
6f45c62815
|
@ -49,6 +49,7 @@ func initDatabase() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fpstore = NewForumPermsStore()
|
||||
|
||||
log.Print("Loading the settings.")
|
||||
err = LoadSettings()
|
||||
|
|
19
extend.go
19
extend.go
|
@ -21,8 +21,6 @@ var hooks = map[string][]func(interface{}) interface{}{
|
|||
|
||||
// Hooks with a variable number of arguments
|
||||
var vhooks = map[string]func(...interface{}) interface{}{
|
||||
"simple_forum_check_pre_perms": nil,
|
||||
"forum_check_pre_perms": nil,
|
||||
"intercept_build_widgets": nil,
|
||||
"forum_trow_assign": nil,
|
||||
"topics_topic_row_assign": nil,
|
||||
|
@ -32,6 +30,14 @@ var vhooks = map[string]func(...interface{}) interface{}{
|
|||
"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:
|
||||
type Message interface {
|
||||
ID() int
|
||||
|
@ -221,6 +227,9 @@ func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
|||
case func(...interface{}) interface{}:
|
||||
vhooks[name] = h
|
||||
plugin.Hooks[name] = 0
|
||||
case func(...interface{}) (bool, RouteError):
|
||||
vhookSkippable[name] = h
|
||||
plugin.Hooks[name] = 0
|
||||
default:
|
||||
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
|
||||
case func(...interface{}) interface{}:
|
||||
delete(vhooks, name)
|
||||
case func(...interface{}) (bool, RouteError):
|
||||
delete(vhookSkippable, name)
|
||||
default:
|
||||
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...)
|
||||
}
|
||||
|
||||
func runVhookSkippable(name string, data ...interface{}) (bool, RouteError) {
|
||||
return vhookSkippable[name](data...)
|
||||
}
|
||||
|
||||
func runVhookNoreturn(name string, data ...interface{}) {
|
||||
_ = vhooks[name](data...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -532,8 +532,8 @@ func buildForumPermissions() error {
|
|||
groups, err := gstore.GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
if dev.DebugMode {
|
||||
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(group.ID) + " - " + group.Name)
|
||||
|
|
|
@ -45,7 +45,7 @@ func deactivateMarkdown() {
|
|||
// 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.
|
||||
func markdownParse(msg string) string {
|
||||
msg = strings.TrimSpace(_markdownParse(msg+" ", 0))
|
||||
msg = strings.TrimSuffix(_markdownParse(msg+" ", 0), " ")
|
||||
log.Print("final msg: ", msg)
|
||||
return msg
|
||||
}
|
||||
|
@ -172,9 +172,9 @@ func _markdownParse(msg string, n int) string {
|
|||
|
||||
index++
|
||||
|
||||
//log.Print("preskip index",index)
|
||||
//log.Print("preskip msg[index]",msg[index])
|
||||
//log.Print("preskip string(msg[index])",string(msg[index]))
|
||||
//log.Print("preskip index: ", index)
|
||||
//log.Print("preskip msg[index]: ", msg[index])
|
||||
//log.Print("preskip string(msg[index]): ", string(msg[index]))
|
||||
index = markdownSkipUntilAsterisk(msg, index)
|
||||
|
||||
if index >= len(msg) {
|
||||
|
@ -222,8 +222,8 @@ func _markdownParse(msg string, n int) string {
|
|||
sIndex++
|
||||
}
|
||||
|
||||
//log.Print("sIndex",sIndex)
|
||||
//log.Print("lIndex",lIndex)
|
||||
//log.Print("sIndex: ", sIndex)
|
||||
//log.Print("lIndex: ", lIndex)
|
||||
|
||||
if lIndex <= sIndex {
|
||||
//log.Print("unclosed markdown element @ lIndex <= sIndex")
|
||||
|
@ -241,19 +241,19 @@ func _markdownParse(msg string, n int) string {
|
|||
break
|
||||
}
|
||||
|
||||
//log.Print("final sIndex",sIndex)
|
||||
//log.Print("final lIndex",lIndex)
|
||||
//log.Print("final index",index)
|
||||
//log.Print("final msg[index]",msg[index])
|
||||
//log.Print("final string(msg[index])",string(msg[index]))
|
||||
//log.Print("final sIndex: ", sIndex)
|
||||
//log.Print("final lIndex: ",lIndex)
|
||||
//log.Print("final index: ", index)
|
||||
//log.Print("final msg[index]: ", msg[index])
|
||||
//log.Print("final string(msg[index]): ", string(msg[index]))
|
||||
|
||||
//log.Print("final msg[sIndex]",msg[sIndex])
|
||||
//log.Print("final string(msg[sIndex])",string(msg[sIndex]))
|
||||
//log.Print("final msg[lIndex]",msg[lIndex])
|
||||
//log.Print("final string(msg[lIndex])",string(msg[lIndex]))
|
||||
//log.Print("final msg[sIndex]: ", msg[sIndex])
|
||||
//log.Print("final string(msg[sIndex]): ", string(msg[sIndex]))
|
||||
//log.Print("final msg[lIndex]: ", msg[lIndex])
|
||||
//log.Print("final string(msg[lIndex]): ", string(msg[lIndex]))
|
||||
|
||||
//log.Print("[]byte(msg[:sIndex])",[]byte(msg[:sIndex]))
|
||||
//log.Print("[]byte(msg[:lIndex])",[]byte(msg[:lIndex]))
|
||||
//log.Print("[]byte(msg[:sIndex]): ", []byte(msg[:sIndex]))
|
||||
//log.Print("[]byte(msg[:lIndex]): ", []byte(msg[:lIndex]))
|
||||
|
||||
outbytes = append(outbytes, msg[lastElement:startIndex]...)
|
||||
|
||||
|
@ -278,13 +278,13 @@ func _markdownParse(msg string, n int) string {
|
|||
index--
|
||||
case '\\':
|
||||
if (index + 1) < len(msg) {
|
||||
if isMarkdownStartChar(msg[index+1]) && msg[index+1] != '\\' {
|
||||
outbytes = append(outbytes, msg[lastElement:index]...)
|
||||
index++
|
||||
lastElement = index
|
||||
}
|
||||
}
|
||||
//case '`':
|
||||
//case '_':
|
||||
//case '~':
|
||||
//case 10: // newline
|
||||
}
|
||||
}
|
||||
|
@ -298,6 +298,10 @@ func _markdownParse(msg string, n int) string {
|
|||
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 {
|
||||
for ; index < len(data); index++ {
|
||||
item := data[index]
|
||||
|
|
|
@ -561,7 +561,7 @@ func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} {
|
|||
// TODO: Add privacy options
|
||||
// TODO: Add support for multiple boards and add per-board simplified permissions
|
||||
// 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 fid = args[3].(*int)
|
||||
var forum = fstore.DirtyGet(*fid)
|
||||
|
@ -569,19 +569,14 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
|||
if forum.ParentType == "socialgroup" {
|
||||
var err error
|
||||
var w = args[0].(http.ResponseWriter)
|
||||
var success = args[4].(*bool)
|
||||
sgItem, ok := r.Context().Value("socialgroups_current_group").(*SocialGroup)
|
||||
if !ok {
|
||||
sgItem, err = socialgroupsGetGroup(forum.ParentID)
|
||||
if err != nil {
|
||||
InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
|
||||
*success = false
|
||||
return false
|
||||
return true, InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
|
||||
}
|
||||
if !sgItem.Active {
|
||||
NotFound(w, r)
|
||||
*success = false
|
||||
return false
|
||||
return true, NotFound(w, r)
|
||||
}
|
||||
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)
|
||||
if err != nil && err != ErrNoRows {
|
||||
*success = false
|
||||
InternalError(err, w, r)
|
||||
return false
|
||||
return true, InternalError(err, w, r)
|
||||
} 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: How does this even work? Refactor it along with the rest of this plugin!
|
||||
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!
|
||||
|
@ -622,10 +617,10 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
|||
} else {
|
||||
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
|
||||
|
|
|
@ -214,6 +214,8 @@ func TestMarkdownRender(t *testing.T) {
|
|||
msgList = addMEPair(msgList, "**hi*", "*<i>hi</i>")
|
||||
msgList = addMEPair(msgList, "***hi***", "<b><i>hi</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, "d\\*hi\\*", "d*hi*")
|
||||
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, "*_hi_*", "<i><u>hi</u></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~_", "<u><s>hi</s></u>")
|
||||
msgList = addMEPair(msgList, "***~hi~***", "<b><i><s>hi</s></i></b>")
|
||||
msgList = addMEPair(msgList, "**", "**")
|
||||
|
@ -255,4 +259,37 @@ func TestMarkdownRender(t *testing.T) {
|
|||
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.
|
||||
}
|
||||
|
|
11
reply.go
11
reply.go
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
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?
|
||||
var rstore ReplyStore
|
||||
|
@ -21,7 +24,8 @@ type ReplyUser struct {
|
|||
UserLink string
|
||||
CreatedByName string
|
||||
Group int
|
||||
CreatedAt string
|
||||
CreatedAt time.Time
|
||||
RelativeCreatedAt string
|
||||
LastEdit int
|
||||
LastEditBy int
|
||||
Avatar string
|
||||
|
@ -45,7 +49,8 @@ type Reply struct {
|
|||
Content string
|
||||
CreatedBy int
|
||||
Group int
|
||||
CreatedAt string
|
||||
CreatedAt time.Time
|
||||
RelativeCreatedAt string
|
||||
LastEdit int
|
||||
LastEditBy int
|
||||
ContentLines int
|
||||
|
|
43
routes.go
43
routes.go
|
@ -533,22 +533,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
if postGroup.IsMod || postGroup.IsAdmin {
|
||||
topic.ClassName = config.StaffCSS
|
||||
}
|
||||
|
||||
/*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 = ""
|
||||
}
|
||||
topic.RelativeCreatedAt = relativeTime(topic.CreatedAt)
|
||||
|
||||
// TODO: Make a function for this? Build a more sophisticated noavatar handling system?
|
||||
if topic.Avatar != "" {
|
||||
|
@ -604,7 +589,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
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[0] == '.' {
|
||||
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
|
||||
|
||||
/*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 = ""
|
||||
}
|
||||
replyItem.RelativeCreatedAt = relativeTime(replyItem.CreatedAt)
|
||||
|
||||
// We really shouldn't have inline HTML, we should do something about this...
|
||||
if replyItem.ActionType != "" {
|
||||
|
@ -683,7 +653,8 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
}
|
||||
|
||||
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 replyList []ReplyUser
|
||||
|
||||
|
@ -736,6 +707,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
} else {
|
||||
replyClassName = ""
|
||||
}
|
||||
|
||||
if replyAvatar != "" {
|
||||
if replyAvatar[0] == '.' {
|
||||
replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar
|
||||
|
@ -754,10 +726,11 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
|
||||
replyLiked := false
|
||||
replyLikeCount := 0
|
||||
replyRelativeCreatedAt = relativeTime(replyCreatedAt)
|
||||
|
||||
// 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()
|
||||
if err != nil {
|
||||
|
|
|
@ -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?
|
||||
if vhooks["simple_forum_check_pre_perms"] != nil {
|
||||
if runVhook("simple_forum_check_pre_perms", w, r, user, &fid, &rerr, &headerLite).(bool) {
|
||||
if vhookSkippable["simple_forum_check_pre_perms"] != nil {
|
||||
var skip bool
|
||||
skip, rerr = runVhookSkippable("simple_forum_check_pre_perms", w, r, user, &fid, &headerLite)
|
||||
if skip {
|
||||
return headerLite, rerr
|
||||
}
|
||||
}
|
||||
|
||||
group, err := gstore.Get(user.Group)
|
||||
fperms, err := fpstore.Get(fid, user.Group)
|
||||
if err != nil {
|
||||
// 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)
|
||||
}
|
||||
|
||||
fperms := group.Forums[fid]
|
||||
cascadeForumPerms(fperms, user)
|
||||
return headerLite, nil
|
||||
}
|
||||
|
||||
func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, ferr RouteError) {
|
||||
headerVars, ferr = UserCheck(w, r, user)
|
||||
if ferr != nil {
|
||||
return headerVars, ferr
|
||||
func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, rerr RouteError) {
|
||||
headerVars, rerr = UserCheck(w, r, user)
|
||||
if rerr != nil {
|
||||
return headerVars, rerr
|
||||
}
|
||||
if !fstore.Exists(fid) {
|
||||
return headerVars, NotFound(w, r)
|
||||
}
|
||||
|
||||
if vhooks["forum_check_pre_perms"] != nil {
|
||||
if runVhook("forum_check_pre_perms", w, r, user, &fid, &ferr, &headerVars).(bool) {
|
||||
return headerVars, ferr
|
||||
if vhookSkippable["forum_check_pre_perms"] != nil {
|
||||
var skip bool
|
||||
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 {
|
||||
// TODO: Refactor this
|
||||
log.Printf("Group #%d doesn't exist despite being used by User #%d", user.Group, user.ID)
|
||||
return headerVars, PreError("Something weird happened", w, r)
|
||||
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)
|
||||
}
|
||||
|
||||
fperms := group.Forums[fid]
|
||||
//log.Printf("user.Perms: %+v\n", user.Perms)
|
||||
//log.Printf("fperms: %+v\n", fperms)
|
||||
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?
|
||||
|
|
|
@ -92,9 +92,10 @@ func compileTemplates() error {
|
|||
|
||||
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
|
||||
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)
|
||||
tpage := TopicPage{"Title", user, headerVars, replyList, topic, 1, 1}
|
||||
|
|
127
template_list.go
127
template_list.go
|
@ -351,7 +351,7 @@ var topic_alt_29 = []byte(`</textarea>
|
|||
<div class="button_container">
|
||||
`)
|
||||
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_33 = []byte(`" class="action_button open_edit">Edit</a>`)
|
||||
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_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_48 = []byte(` up</a>`)
|
||||
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_47 = []byte(`
|
||||
<div class="action_button_right">
|
||||
`)
|
||||
var topic_alt_51 = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`)
|
||||
var topic_alt_52 = []byte(`</a>`)
|
||||
var topic_alt_53 = []byte(`
|
||||
var topic_alt_48 = []byte(`<a class="action_button like_count hide_on_micro">`)
|
||||
var topic_alt_49 = []byte(`</a>`)
|
||||
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 style="clear:both;"></div>
|
||||
</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 `)
|
||||
var topic_alt_55 = []byte(`action_item`)
|
||||
var topic_alt_56 = []byte(`">
|
||||
var topic_alt_56 = []byte(`action_item`)
|
||||
var topic_alt_57 = []byte(`">
|
||||
<div class="userinfo" aria-label="The information on the poster">
|
||||
<div class="avatar_item" style="background-image: url(`)
|
||||
var topic_alt_57 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;"> </div>
|
||||
var topic_alt_58 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;"> </div>
|
||||
<a href="`)
|
||||
var topic_alt_58 = []byte(`" class="the_name" rel="author">`)
|
||||
var topic_alt_59 = []byte(`</a>
|
||||
var topic_alt_59 = []byte(`" class="the_name" rel="author">`)
|
||||
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><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><div class="tag_post"></div></div>`)
|
||||
var topic_alt_64 = []byte(`
|
||||
var topic_alt_61 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
|
||||
var topic_alt_62 = []byte(`</div><div class="tag_post"></div></div>`)
|
||||
var topic_alt_63 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
|
||||
var topic_alt_64 = []byte(`</div><div class="tag_post"></div></div>`)
|
||||
var topic_alt_65 = []byte(`
|
||||
</div>
|
||||
<div class="content_container" `)
|
||||
var topic_alt_65 = []byte(`style="margin-left: 0px;"`)
|
||||
var topic_alt_66 = []byte(`>
|
||||
var topic_alt_66 = []byte(`style="margin-left: 0px;"`)
|
||||
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;">`)
|
||||
var topic_alt_68 = []byte(`</span>
|
||||
<span itemprop="text">`)
|
||||
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">`)
|
||||
var topic_alt_71 = []byte(`</div>
|
||||
var topic_alt_72 = []byte(`</div>
|
||||
<div class="button_container">
|
||||
`)
|
||||
var topic_alt_72 = []byte(`<a href="/reply/like/submit/`)
|
||||
var topic_alt_73 = []byte(`" class="action_button">+1</a>`)
|
||||
var topic_alt_74 = []byte(`<a href="/reply/edit/submit/`)
|
||||
var topic_alt_75 = []byte(`" class="action_button edit_item">Edit</a>`)
|
||||
var topic_alt_76 = []byte(`<a href="/reply/delete/submit/`)
|
||||
var topic_alt_77 = []byte(`" class="action_button delete_item">Delete</a>`)
|
||||
var topic_alt_78 = []byte(`
|
||||
var topic_alt_73 = []byte(`<a href="/reply/like/submit/`)
|
||||
var topic_alt_74 = []byte(`" class="action_button like_item">+1</a>`)
|
||||
var topic_alt_75 = []byte(`<a href="/reply/edit/submit/`)
|
||||
var topic_alt_76 = []byte(`" class="action_button edit_item">Edit</a>`)
|
||||
var topic_alt_77 = []byte(`<a href="/reply/delete/submit/`)
|
||||
var topic_alt_78 = []byte(`" class="action_button delete_item">Delete</a>`)
|
||||
var topic_alt_79 = []byte(`
|
||||
<a href="/report/submit/`)
|
||||
var topic_alt_79 = []byte(`?session=`)
|
||||
var topic_alt_80 = []byte(`&type=reply" class="action_button report_item">Report</a>
|
||||
var topic_alt_80 = []byte(`?session=`)
|
||||
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(` up</a>`)
|
||||
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_82 = []byte(`
|
||||
<div class="action_button_right">
|
||||
`)
|
||||
var topic_alt_85 = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`)
|
||||
var topic_alt_86 = []byte(`</a>`)
|
||||
var topic_alt_87 = []byte(`
|
||||
var topic_alt_83 = []byte(`<a class="action_button like_count hide_on_micro">`)
|
||||
var topic_alt_84 = []byte(`</a>`)
|
||||
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>
|
||||
`)
|
||||
var topic_alt_88 = []byte(`
|
||||
var topic_alt_90 = []byte(`
|
||||
</div>
|
||||
<div style="clear:both;"></div>
|
||||
</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;"> </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">
|
||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||
<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="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 class="formrow quick_button_row">
|
||||
<div class="formitem">
|
||||
<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;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topic_alt_93 = []byte(`
|
||||
var topic_alt_103 = []byte(`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topic_alt_94 = []byte(`
|
||||
var topic_alt_104 = []byte(`
|
||||
|
||||
</main>
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
// 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. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
@ -182,108 +182,126 @@ w.Write(topic_alt_45)
|
|||
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
|
||||
w.Write(topic_alt_46)
|
||||
}
|
||||
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
|
||||
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([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
|
||||
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_54)
|
||||
if len(tmpl_topic_alt_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_topic_alt_vars.ItemList {
|
||||
w.Write(topic_alt_54)
|
||||
if item.ActionType != "" {
|
||||
w.Write(topic_alt_55)
|
||||
}
|
||||
if item.ActionType != "" {
|
||||
w.Write(topic_alt_56)
|
||||
w.Write([]byte(item.Avatar))
|
||||
}
|
||||
w.Write(topic_alt_57)
|
||||
w.Write([]byte(item.UserLink))
|
||||
w.Write([]byte(item.Avatar))
|
||||
w.Write(topic_alt_58)
|
||||
w.Write([]byte(item.CreatedByName))
|
||||
w.Write([]byte(item.UserLink))
|
||||
w.Write(topic_alt_59)
|
||||
if item.Tag != "" {
|
||||
w.Write([]byte(item.CreatedByName))
|
||||
w.Write(topic_alt_60)
|
||||
w.Write([]byte(item.Tag))
|
||||
if item.Tag != "" {
|
||||
w.Write(topic_alt_61)
|
||||
} else {
|
||||
w.Write([]byte(item.Tag))
|
||||
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 {
|
||||
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([]byte(item.ContentHtml))
|
||||
} else {
|
||||
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.Perms.LikeItem {
|
||||
w.Write(topic_alt_72)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
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 {
|
||||
w.Write(topic_alt_74)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
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 {
|
||||
w.Write(topic_alt_76)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_alt_77)
|
||||
}
|
||||
w.Write(topic_alt_78)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_alt_78)
|
||||
}
|
||||
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)
|
||||
}
|
||||
if item.LikeCount > 0 {
|
||||
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
|
||||
w.Write(topic_alt_81)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
}
|
||||
w.Write(topic_alt_82)
|
||||
}
|
||||
if item.LikeCount > 0 {
|
||||
w.Write(topic_alt_83)
|
||||
w.Write([]byte(item.CreatedAt))
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topic_alt_84)
|
||||
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
|
||||
}
|
||||
w.Write(topic_alt_85)
|
||||
w.Write([]byte(item.IPAddress))
|
||||
w.Write([]byte(item.RelativeCreatedAt))
|
||||
w.Write(topic_alt_86)
|
||||
}
|
||||
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
|
||||
w.Write(topic_alt_87)
|
||||
}
|
||||
w.Write([]byte(item.IPAddress))
|
||||
w.Write(topic_alt_88)
|
||||
}
|
||||
}
|
||||
w.Write(topic_alt_89)
|
||||
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
|
||||
}
|
||||
w.Write(topic_alt_90)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
|
||||
}
|
||||
}
|
||||
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([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar))
|
||||
w.Write(topic_alt_93)
|
||||
}
|
||||
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
|
||||
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)
|
||||
if len(tmpl_topic_alt_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_topic_alt_vars.Header.Themes {
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
|
||||
<div class="button_container">
|
||||
{{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.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}}
|
||||
{{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}}
|
||||
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item">Report</a>
|
||||
{{end}}
|
||||
{{if .Topic.LikeCount}}<a class="action_button action_button_right like_count hide_on_micro">{{.Topic.LikeCount}} up</a>{{end}}
|
||||
<a class="action_button action_button_right created_at hide_on_mobile">{{.Topic.CreatedAt}}</a>
|
||||
{{if .CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.Topic.IPAddress}}</a>{{end}}
|
||||
<div class="action_button_right">
|
||||
{{if .Topic.LikeCount}}<a class="action_button like_count hide_on_micro">{{.Topic.LikeCount}}</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 style="clear:both;"></div>
|
||||
</article>
|
||||
|
@ -66,14 +68,16 @@
|
|||
<div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div>
|
||||
<div class="button_container">
|
||||
{{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.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>
|
||||
{{end}}
|
||||
{{if .LikeCount}}<a class="action_button action_button_right like_count hide_on_micro">{{.LikeCount}} up</a>{{end}}
|
||||
<a class="action_button action_button_right created_at hide_on_mobile">{{.CreatedAt}}</a>
|
||||
{{if $.CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.IPAddress}}</a>{{end}}
|
||||
<div class="action_button_right">
|
||||
{{if .LikeCount}}<a class="action_button like_count hide_on_micro">{{.LikeCount}}</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>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -82,12 +86,18 @@
|
|||
{{end}}</div>
|
||||
|
||||
{{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;"> </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">
|
||||
<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" />
|
||||
<div class="formrow real_first_child">
|
||||
<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 class="formrow quick_button_row">
|
||||
|
@ -100,6 +110,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
</main>
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
--primary-link-color: hsl(0,0%,40%);
|
||||
--primary-text-color: hsl(0,0%,20%);
|
||||
--lightened-primary-text-color: hsl(0,0%,30%);
|
||||
--extra-lightened-primary-text-color: hsl(0,0%,40%);
|
||||
--inverse-primary-text-color: white;
|
||||
--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%);
|
||||
}
|
||||
|
||||
.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 {
|
||||
display: none;
|
||||
}
|
||||
|
@ -600,33 +621,67 @@ select, input, textarea {
|
|||
}
|
||||
.userinfo {
|
||||
margin-right: 16px;
|
||||
padding: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
padding-bottom: 12px;
|
||||
padding-top: 30px;
|
||||
padding-left: 42px;
|
||||
padding-right: 42px;
|
||||
padding-bottom: 18px;
|
||||
height: min-content;
|
||||
/*overflow: hidden;
|
||||
text-overflow: ellipsis;*/
|
||||
}
|
||||
.content_container {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
padding: 17px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.avatar_item {
|
||||
border-radius: 50px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
margin-bottom: 5px;
|
||||
background-size: 128px;
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
margin-bottom: 12px;
|
||||
background-size: 120px;
|
||||
}
|
||||
.the_name, .userinfo .tag_block {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.the_name {
|
||||
font-size: 19px;
|
||||
font-size: 18px;
|
||||
color: var(--lightened-primary-text-color);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.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-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) {
|
||||
|
|
|
@ -652,6 +652,9 @@ button.username {
|
|||
padding-right: 7px;
|
||||
}
|
||||
.action_button_right {
|
||||
display: flex;
|
||||
}
|
||||
.action_button_right .action_button {
|
||||
border-left: solid 1px #eaeaea;
|
||||
border-right: none;
|
||||
}
|
||||
|
@ -660,10 +663,21 @@ button.username {
|
|||
margin-right: auto;
|
||||
}
|
||||
|
||||
.like_label:before { content: "😀"; }
|
||||
.edit_label:before { content: "🖊️"; }
|
||||
.trash_label:before { content: "🗑️"; }
|
||||
.flag_label:before { content: "🚩"; }
|
||||
.like_label:before {
|
||||
content: "😀";
|
||||
}
|
||||
.like_count:after {
|
||||
content: " up";
|
||||
}
|
||||
.edit_label:before {
|
||||
content: "🖊️";
|
||||
}
|
||||
.trash_label:before {
|
||||
content: "🗑️";
|
||||
}
|
||||
.flag_label:before {
|
||||
content: "🚩";
|
||||
}
|
||||
|
||||
.mod_button {
|
||||
margin-right: 4px;
|
||||
|
|
6
topic.go
6
topic.go
|
@ -24,7 +24,8 @@ type Topic struct {
|
|||
CreatedBy int
|
||||
IsClosed bool
|
||||
Sticky bool
|
||||
CreatedAt string
|
||||
CreatedAt time.Time
|
||||
RelativeCreatedAt string
|
||||
LastReplyAt time.Time
|
||||
RelativeLastReplyAt string
|
||||
//LastReplyBy int
|
||||
|
@ -45,7 +46,8 @@ type TopicUser struct {
|
|||
CreatedBy int
|
||||
IsClosed bool
|
||||
Sticky bool
|
||||
CreatedAt string
|
||||
CreatedAt time.Time
|
||||
RelativeCreatedAt string
|
||||
LastReplyAt time.Time
|
||||
RelativeLastReplyAt string
|
||||
//LastReplyBy int
|
||||
|
|
Loading…
Reference in New Issue