implement HooksNoRet and HooksSkip

use hookgen for route_forum_list_start
use hookgen for forums_frow_assign
change forums_frow_assign to a noret hook
save bytes and reduce boilerplate
This commit is contained in:
Azareal 2020-05-26 17:53:56 +10:00
parent 67772f2f9a
commit 07d478179d
15 changed files with 141 additions and 91 deletions

View File

@ -19,11 +19,15 @@ type Hook struct {
Ret string Ret string
Type string Type string
Any bool Any bool
MultiHook bool
Skip bool
DefaultRet string
Pure string
} }
func AddHooks(add func(name, params, ret, htype string)) { func AddHooks(add func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string)) {
vhookskip := func(name, params string) { vhookskip := func(name, params string) {
add(name,params,"(bool, RouteError)","VhookSkippable_") add(name,params,"(bool,RouteError)","VhookSkippable_",false,true,"false,nil","")
} }
vhookskip("forum_check_pre_perms","w http.ResponseWriter,r *http.Request,u *User,fid *int,h *Header") vhookskip("forum_check_pre_perms","w http.ResponseWriter,r *http.Request,u *User,fid *int,h *Header")
vhookskip("router_after_filters","w http.ResponseWriter,r *http.Request,prefix string") vhookskip("router_after_filters","w http.ResponseWriter,r *http.Request,prefix string")
@ -31,10 +35,24 @@ func AddHooks(add func(name, params, ret, htype string)) {
vhookskip("route_forum_list_start","w http.ResponseWriter,r *http.Request,u *User,h *Header") vhookskip("route_forum_list_start","w http.ResponseWriter,r *http.Request,u *User,h *Header")
vhookskip("route_topic_list_start","w http.ResponseWriter,r *http.Request,u *User,h *Header") vhookskip("route_topic_list_start","w http.ResponseWriter,r *http.Request,u *User,h *Header")
vhooknoret := func(name, params string) { vhooknoret := func(name, params string) {
add(name,params,"","Vhooks") add(name,params,"","Vhooks",false,false,"false,nil","")
} }
vhooknoret("router_end","w http.ResponseWriter,r *http.Request,u *User,prefix string, extraData string") vhooknoret("router_end","w http.ResponseWriter,r *http.Request,u *User,prefix string, extraData string")
vhooknoret("topic_reply_row_assign","r *ReplyUser") vhooknoret("topic_reply_row_assign","r *ReplyUser")
//forums_frow_assign
//Hook(name string, data interface{}) interface{}
/*hook := func(name, params, ret, pure string) {
add(name,params,ret,"Hooks",true,false,ret,pure)
}*/
hooknoret := func(name, params string) {
add(name,params,"","HooksNoRet",true,false,"","")
}
hooknoret("forums_frow_assign","f *Forum")
hookskip := func(name, params string) {
add(name,params,"(skip bool)","HooksSkip",true,true,"","")
}
//hookskip("forums_frow_assign","f *Forum")
hookskip("topic_create_frow_assign","f *Forum")
} }
func Write(hookVars HookVars) { func Write(hookVars HookVars) {
@ -45,12 +63,17 @@ import ({{range .Imports}}
"{{.}}"{{end}} "{{.}}"{{end}}
) )
{{range .Hooks}} {{range .Hooks}}
func H_{{.Name}}_hook(t *HookTable, {{.Params}}) {{.Ret}} { {{if .Any}} func H_{{.Name}}_hook(t *HookTable,{{.Params}}) {{.Ret}} { {{if .Any}}
hook := t.{{.Type}}["{{.Name}}"] {{if .MultiHook}}for _, hook := range t.{{.Type}}["{{.Name}}"] {
{{if .Skip}}if skip = hook({{.Params2}}); skip {
break
}{{else}}{{if .Pure}}{{.Pure}} = {{else if .Ret}}return {{end}}hook({{.Params2}}){{end}}
}{{else}}hook := t.{{.Type}}["{{.Name}}"]
if hook != nil { if hook != nil {
{{if .Ret}}return {{end}}hook({{.Params2}}) {{if .Ret}}return {{end}}hook({{.Params2}})
} {{end}} } {{end}}{{end}}{{if .Pure}}
{{if .Ret}}return false, nil{{end}} return {{.Pure}}{{else if .Ret}}
return {{.DefaultRet}}{{end}}
}{{end}} }{{end}}
` `
tmpl := template.Must(template.New("hooks").Parse(fileData)) tmpl := template.Must(template.New("hooks").Parse(fileData))

View File

@ -20,7 +20,6 @@ func main() {
if r := recover(); r != nil { if r := recover(); r != nil {
fmt.Println(r) fmt.Println(r)
debug.PrintStack() debug.PrintStack()
return
} }
}() }()
@ -49,7 +48,7 @@ func main() {
imports := []string{"net/http"} imports := []string{"net/http"}
hookVars := h.HookVars{imports,nil} hookVars := h.HookVars{imports,nil}
add := func(name, params, ret, htype string) { add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) {
var params2 string var params2 string
first := true first := true
for _, param := range strings.Split(params,",") { for _, param := range strings.Split(params,",") {
@ -60,7 +59,7 @@ func main() {
params2 += pspl[0] params2 += pspl[0]
first = false first = false
} }
hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, hooks[name] > 0}) hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, hooks[name] > 0, multiHook, skip, defaultRet, pure})
} }
h.AddHooks(add) h.AddHooks(add)

View File

@ -16,13 +16,12 @@ func main() {
if r := recover(); r != nil { if r := recover(); r != nil {
fmt.Println(r) fmt.Println(r)
debug.PrintStack() debug.PrintStack()
return
} }
}() }()
imports := []string{"net/http"} imports := []string{"net/http"}
hookVars := h.HookVars{imports,nil} hookVars := h.HookVars{imports,nil}
add := func(name, params, ret, htype string) { add := func(name, params, ret, htype string, multiHook, skip bool, defaultRet, pure string) {
var params2 string var params2 string
first := true first := true
for _, param := range strings.Split(params,",") { for _, param := range strings.Split(params,",") {
@ -33,7 +32,7 @@ func main() {
params2 += pspl[0] params2 += pspl[0]
first = false first = false
} }
hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, true}) hookVars.Hooks = append(hookVars.Hooks, h.Hook{name, params, params2, ret, htype, true, multiHook, skip, defaultRet,pure})
} }
h.AddHooks(add) h.AddHooks(add)

View File

@ -49,14 +49,16 @@ var hookTableBox atomic.Value
// TODO: Make the RunXHook functions methods on HookTable // TODO: Make the RunXHook functions methods on HookTable
// TODO: Have plugins update hooks on a mutex guarded map and create a copy of that map in a serial global goroutine which gets thrown in the atomic.Value // TODO: Have plugins update hooks on a mutex guarded map and create a copy of that map in a serial global goroutine which gets thrown in the atomic.Value
type HookTable struct { type HookTable struct {
Hooks map[string][]func(interface{}) interface{} //Hooks map[string][]func(interface{}) interface{}
HooksNoRet map[string][]func(interface{})
HooksSkip map[string][]func(interface{}) bool
Vhooks map[string]func(...interface{}) interface{} Vhooks map[string]func(...interface{}) interface{}
VhookSkippable_ map[string]func(...interface{}) (bool, RouteError) VhookSkippable_ map[string]func(...interface{}) (bool, RouteError)
Sshooks map[string][]func(string) string Sshooks map[string][]func(string) string
PreRenderHooks map[string][]func(http.ResponseWriter, *http.Request, *User, interface{}) bool PreRenderHooks map[string][]func(http.ResponseWriter, *http.Request, *User, interface{}) bool
// For future use: // For future use:
messageHooks map[string][]func(Message, PageInt, ...interface{}) interface{} //messageHooks map[string][]func(Message, PageInt, ...interface{}) interface{}
} }
func init() { func init() {
@ -65,15 +67,18 @@ func init() {
// For extend.go use only, access this via GetHookTable() elsewhere // For extend.go use only, access this via GetHookTable() elsewhere
var hookTable = &HookTable{ var hookTable = &HookTable{
map[string][]func(interface{}) interface{}{ //map[string][]func(interface{}) interface{}{},
"forums_frow_assign": nil, map[string][]func(interface{}){
"topic_create_frow_assign": nil, "forums_frow_assign": nil, //hg
},
map[string][]func(interface{}) bool{
"topic_create_frow_assign": nil, //hg
}, },
map[string]func(...interface{}) interface{}{ map[string]func(...interface{}) interface{}{
//"convo_post_update":nil, //"convo_post_update":nil,
//"convo_post_create":nil, //"convo_post_create":nil,
"forum_trow_assign": nil, ///"forum_trow_assign": nil,
"topics_topic_row_assign": nil, "topics_topic_row_assign": nil,
//"topics_user_row_assign": nil, //"topics_user_row_assign": nil,
"topic_reply_row_assign": nil, "topic_reply_row_assign": nil,
@ -83,8 +88,8 @@ var hookTable = &HookTable{
"router_end": nil, "router_end": nil,
}, },
map[string]func(...interface{}) (bool, RouteError){ map[string]func(...interface{}) (bool, RouteError){
"simple_forum_check_pre_perms": nil, "simple_forum_check_pre_perms": nil, //hg
"forum_check_pre_perms": nil, "forum_check_pre_perms": nil, //hg
"route_topic_list_start": nil, "route_topic_list_start": nil,
"route_topic_list_mostviewed_start": nil, "route_topic_list_mostviewed_start": nil,
@ -125,7 +130,7 @@ var hookTable = &HookTable{
"parse_assign": nil, "parse_assign": nil,
}, },
nil, nil,
nil, //nil,
} }
var hookTableUpdateMutex sync.Mutex var hookTableUpdateMutex sync.Mutex
@ -146,51 +151,47 @@ func GetHookTable() *HookTable {
} }
// Hooks with a single argument. Is this redundant? Might be useful for inlining, as variadics aren't inlined? Are closures even inlined to begin with? // Hooks with a single argument. Is this redundant? Might be useful for inlining, as variadics aren't inlined? Are closures even inlined to begin with?
func (t *HookTable) Hook(name string, data interface{}) interface{} { /*func (t *HookTable) Hook(name string, data interface{}) interface{} {
hooks, ok := t.Hooks[name] for _, hook := range t.Hooks[name] {
if ok {
for _, hook := range hooks {
data = hook(data) data = hook(data)
} }
}
return data return data
}*/
func (t *HookTable) HookNoRet(name string, data interface{}) {
for _, hook := range t.HooksNoRet[name] {
hook(data)
}
} }
// To cover the case in routes/topic.go's CreateTopic route, we could probably obsolete this use and replace it // To cover the case in routes/topic.go's CreateTopic route, we could probably obsolete this use and replace it
func (t *HookTable) HookSkippable(name string, data interface{}) (skip bool) { func (t *HookTable) HookSkip(name string, data interface{}) (skip bool) {
hooks, ok := t.Hooks[name] for _, hook := range t.HooksSkip[name] {
if ok { if skip = hook(data); skip {
for _, hook := range hooks {
skip = hook(data).(bool)
if skip {
break break
} }
} }
}
return skip return skip
} }
// Hooks with a variable number of arguments // Hooks with a variable number of arguments
// TODO: Use RunHook semantics to allow multiple lined up plugins / modules their turn? // TODO: Use RunHook semantics to allow multiple lined up plugins / modules their turn?
func (t *HookTable) Vhook(name string, data ...interface{}) interface{} { func (t *HookTable) Vhook(name string, data ...interface{}) interface{} {
hook := t.Vhooks[name] if hook := t.Vhooks[name]; hook != nil {
if hook != nil {
return hook(data...) return hook(data...)
} }
return nil return nil
} }
func (t *HookTable) VhookNoRet(name string, data ...interface{}) { func (t *HookTable) VhookNoRet(name string, data ...interface{}) {
hook := t.Vhooks[name] if hook := t.Vhooks[name]; hook != nil {
if hook != nil {
_ = hook(data...) _ = hook(data...)
} }
} }
// TODO: Find a better way of doing this // TODO: Find a better way of doing this
func (t *HookTable) VhookNeedHook(name string, data ...interface{}) (ret interface{}, hasHook bool) { func (t *HookTable) VhookNeedHook(name string, data ...interface{}) (ret interface{}, hasHook bool) {
hook := t.Vhooks[name] if hook := t.Vhooks[name]; hook != nil {
if hook != nil {
return hook(data...), true return hook(data...), true
} }
return nil, false return nil, false
@ -198,16 +199,14 @@ func (t *HookTable) VhookNeedHook(name string, data ...interface{}) (ret interfa
// Hooks with a variable number of arguments and return values for skipping the parent function and propagating an error upwards // Hooks with a variable number of arguments and return values for skipping the parent function and propagating an error upwards
func (t *HookTable) VhookSkippable(name string, data ...interface{}) (bool, RouteError) { func (t *HookTable) VhookSkippable(name string, data ...interface{}) (bool, RouteError) {
hook := t.VhookSkippable_[name] if hook := t.VhookSkippable_[name]; hook != nil {
if hook != nil {
return hook(data...) return hook(data...)
} }
return false, nil return false, nil
} }
/*func VhookSkippableTest(t *HookTable, name string, data ...interface{}) (bool, RouteError) { /*func VhookSkippableTest(t *HookTable, name string, data ...interface{}) (bool, RouteError) {
hook := t.VhookSkippable_[name] if hook := t.VhookSkippable_[name]; hook != nil {
if hook != nil {
return hook(data...) return hook(data...)
} }
return false, nil return false, nil
@ -224,12 +223,9 @@ func forum_check_pre_perms_hook(t *HookTable, w http.ResponseWriter, r *http.Req
// Hooks which take in and spit out a string. This is usually used for parser components // Hooks which take in and spit out a string. This is usually used for parser components
// Trying to get a teeny bit of type-safety where-ever possible, especially for such a critical set of hooks // Trying to get a teeny bit of type-safety where-ever possible, especially for such a critical set of hooks
func (t *HookTable) Sshook(name, data string) string { func (t *HookTable) Sshook(name, data string) string {
ssHooks, ok := t.Sshooks[name] for _, hook := range t.Sshooks[name] {
if ok {
for _, hook := range ssHooks {
data = hook(data) data = hook(data)
} }
}
return data return data
} }
@ -472,12 +468,24 @@ func (pl *Plugin) AddHook(name string, hInt interface{}) {
defer hookTableUpdateMutex.Unlock() defer hookTableUpdateMutex.Unlock()
switch h := hInt.(type) { switch h := hInt.(type) {
case func(interface{}) interface{}: /*case func(interface{}) interface{}:
if len(hookTable.Hooks[name]) == 0 { if len(hookTable.Hooks[name]) == 0 {
hookTable.Hooks[name] = []func(interface{}) interface{}{} hookTable.Hooks[name] = []func(interface{}) interface{}{}
} }
hookTable.Hooks[name] = append(hookTable.Hooks[name], h) hookTable.Hooks[name] = append(hookTable.Hooks[name], h)
pl.Hooks[name] = len(hookTable.Hooks[name]) - 1 pl.Hooks[name] = len(hookTable.Hooks[name]) - 1*/
case func(interface{}):
if len(hookTable.HooksNoRet[name]) == 0 {
hookTable.HooksNoRet[name] = []func(interface{}){}
}
hookTable.HooksNoRet[name] = append(hookTable.HooksNoRet[name], h)
pl.Hooks[name] = len(hookTable.HooksNoRet[name]) - 1
case func(interface{}) bool:
if len(hookTable.HooksSkip[name]) == 0 {
hookTable.HooksSkip[name] = []func(interface{}) bool{}
}
hookTable.HooksSkip[name] = append(hookTable.HooksSkip[name], h)
pl.Hooks[name] = len(hookTable.HooksSkip[name]) - 1
case func(string) string: case func(string) string:
if len(hookTable.Sshooks[name]) == 0 { if len(hookTable.Sshooks[name]) == 0 {
hookTable.Sshooks[name] = []func(string) string{} hookTable.Sshooks[name] = []func(string) string{}
@ -521,14 +529,30 @@ func (pl *Plugin) RemoveHook(name string, hInt interface{}) {
} }
switch hInt.(type) { switch hInt.(type) {
case func(interface{}) interface{}: /*case func(interface{}) interface{}:
hook := hookTable.Hooks[name] hook := hookTable.Hooks[name]
if len(hook) == 1 { if len(hook) == 1 {
hook = []func(interface{}) interface{}{} hook = []func(interface{}) interface{}{}
} else { } else {
hook = append(hook[:key], hook[key+1:]...) hook = append(hook[:key], hook[key+1:]...)
} }
hookTable.Hooks[name] = hook hookTable.Hooks[name] = hook*/
case func(interface{}):
hook := hookTable.HooksNoRet[name]
if len(hook) == 1 {
hook = []func(interface{}){}
} else {
hook = append(hook[:key], hook[key+1:]...)
}
hookTable.HooksNoRet[name] = hook
case func(interface{}) bool:
hook := hookTable.HooksSkip[name]
if len(hook) == 1 {
hook = []func(interface{}) bool{}
} else {
hook = append(hook[:key], hook[key+1:]...)
}
hookTable.HooksSkip[name] = hook
case func(string) string: case func(string) string:
hook := hookTable.Sshooks[name] hook := hookTable.Sshooks[name]
if len(hook) == 1 { if len(hook) == 1 {
@ -589,8 +613,8 @@ func InitPlugins() {
// ? - Are the following functions racey? // ? - Are the following functions racey?
func RunTaskHook(name string) error { func RunTaskHook(name string) error {
for _, hook := range taskHooks[name] { for _, hook := range taskHooks[name] {
if err := hook(); err != nil { if e := hook(); e != nil {
return err return e
} }
} }
return nil return nil

View File

@ -162,7 +162,7 @@ func GuildWidgets(header *c.Header, guildItem *Guild) (success bool) {
*/ */
func RouteGuildList(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { func RouteGuildList(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
h, ferr := c.UserCheck(w, r, &user) h, ferr := c.UserCheck(w, r, user)
if ferr != nil { if ferr != nil {
return ferr return ferr
} }
@ -216,7 +216,7 @@ func MiddleViewGuild(w http.ResponseWriter, r *http.Request, user *c.User) c.Rou
} }
func RouteCreateGuild(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { func RouteCreateGuild(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
h, ferr := c.UserCheck(w, r, &user) h, ferr := c.UserCheck(w, r, user)
if ferr != nil { if ferr != nil {
return ferr return ferr
} }
@ -334,7 +334,7 @@ func RouteMemberList(w http.ResponseWriter, r *http.Request, user *c.User) c.Rou
pi := MemberListPage{"Guild Member List", user, header, gMembers, guild, 0, 0} pi := MemberListPage{"Guild Member List", user, header, gMembers, guild, 0, 0}
// A plugin with plugins. Pluginception! // A plugin with plugins. Pluginception!
if c.RunPreRenderHook("pre_render_guilds_member_list", w, r, &user, &pi) { if c.RunPreRenderHook("pre_render_guilds_member_list", w, r, user, &pi) {
return nil return nil
} }
err = c.RunThemeTemplate(header.Theme.Name, "guilds_member_list", pi, w) err = c.RunThemeTemplate(header.Theme.Name, "guilds_member_list", pi, w)
@ -373,7 +373,7 @@ func PreRenderViewForum(w http.ResponseWriter, r *http.Request, user *c.User, da
guildItem := guildData.(*Guild) guildItem := guildData.(*Guild)
guildpi := Page{pi.Title, pi.Header, pi.ItemList, pi.Forum, guildItem, pi.Page, pi.LastPage} guildpi := Page{pi.Title, pi.Header, pi.ItemList, pi.Forum, guildItem, pi.Page, pi.LastPage}
err := routes.RenderTemplate("guilds_view_guild", w, r, header, guildpi) err := routes.RenderTemplate("guilds_view_guild", w, r, pi.Header, guildpi)
if err != nil { if err != nil {
c.LogError(err) c.LogError(err)
return false return false
@ -442,13 +442,13 @@ func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) {
return true, c.InternalError(err, w, r) return true, c.InternalError(err, w, r)
} else if err != nil { } else if err != nil {
// TODO: Should we let admins / guests into public groups? // TODO: Should we let admins / guests into public groups?
return true, c.LocalError("You're not part of this group!", w, r, *user) return true, c.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! // TODO: How does this even work? Refactor it along with the rest of this plugin!
if rank < 0 { if rank < 0 {
return true, c.LocalError("You've been banned from this group!", w, r, *user) return true, c.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!

View File

@ -2,7 +2,7 @@ package main
import ( import (
c "github.com/Azareal/Gosora/common" c "github.com/Azareal/Gosora/common"
"github.com/Azareal/Gosora/extend/guilds/lib" guilds "github.com/Azareal/Gosora/extend/guilds/lib"
) )
// TODO: Add a better way of splitting up giant plugins like this // TODO: Add a better way of splitting up giant plugins like this
@ -54,7 +54,7 @@ func initGuilds(pl *c.Plugin) (err error) {
return acc.FirstError() return acc.FirstError()
} }
func deactivateGuilds(pl *common.Plugin) { func deactivateGuilds(pl *c.Plugin) {
pl.RemoveHook("intercept_build_widgets", guilds.Widgets) pl.RemoveHook("intercept_build_widgets", guilds.Widgets)
pl.RemoveHook("trow_assign", guilds.TrowAssign) pl.RemoveHook("trow_assign", guilds.TrowAssign)
pl.RemoveHook("topic_create_pre_loop", guilds.TopicCreatePreLoop) pl.RemoveHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
@ -77,7 +77,8 @@ func deactivateGuilds(pl *common.Plugin) {
// TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process // TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process
type tC = qgen.DBTableColumn type tC = qgen.DBTableColumn
func installGuilds(plugin *common.Plugin) error {
func installGuilds(plugin *c.Plugin) error {
guildTableStmt, err := qgen.Builder.CreateTable("guilds", "utf8mb4", "utf8mb4_general_ci", guildTableStmt, err := qgen.Builder.CreateTable("guilds", "utf8mb4", "utf8mb4_general_ci",
[]tC{ []tC{
tC{"guildID", "int", 0, false, true, ""}, tC{"guildID", "int", 0, false, true, ""},

View File

@ -9,7 +9,11 @@ import (
) )
func ForumList(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header) c.RouteError { func ForumList(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header) c.RouteError {
skip, rerr := h.Hooks.VhookSkippable("route_forum_list_start", w, r, user, h) /*skip, rerr := h.Hooks.VhookSkippable("route_forum_list_start", w, r, user, h)
if skip || rerr != nil {
return rerr
}*/
skip, rerr := c.H_route_forum_list_start_hook(h.Hooks, w, r, user, h)
if skip || rerr != nil { if skip || rerr != nil {
return rerr return rerr
} }
@ -44,7 +48,8 @@ func ForumList(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header
f.LastTopicTime = c.RelativeTime(f.LastTopic.LastReplyAt) f.LastTopicTime = c.RelativeTime(f.LastTopic.LastReplyAt)
} }
} }
h.Hooks.Hook("forums_frow_assign", &f) //h.Hooks.Hook("forums_frow_assign", &f)
c.H_forums_frow_assign_hook(h.Hooks, &f)
forumList = append(forumList, f) forumList = append(forumList, f)
} }
} }

View File

@ -1,2 +1 @@
{{if .Avatar}}<div class='alertItem withAvatar'style='background-image:url("{{.Avatar}}");'><img src='{{.Avatar}}'class='bgsub'><a class='text'data-asid='{{.ASID}}'href="{{.Path}}">{{.Message}}</a></div>{{else}} {{if .Avatar}}<div class='alertItem withAvatar'style='background-image:url("{{.Avatar}}");'><img src='{{.Avatar}}'class='bgsub'><a class='text'data-asid='{{.ASID}}'href="{{.Path}}">{{.Message}}</a></div>{{else}}<div class='alertItem'><a href="{{.Path}}"class='text'>{{.Message}}</a></div>{{end}}
<div class='alertItem'><a href="{{.Path}}"class='text'>{{.Message}}</a></div>{{end}}

View File

@ -13,7 +13,7 @@
<div class="formitem"><textarea name="body"></textarea></div> <div class="formitem"><textarea name="body"></textarea></div>
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem"><button name="panel-button" class="formbutton form_middle_button">{{lang "create_convo_button"}}</button></div> <div class="formitem"><button name="panel-button"class="formbutton form_middle_button">{{lang "create_convo_button"}}</button></div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -13,7 +13,7 @@
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a>{{lang "create_topic_name"}}</a></div> <div class="formitem formlabel"><a>{{lang "create_topic_name"}}</a></div>
<div class="formitem"><input form="quick_post_form" name="name" type="text" placeholder="{{lang "create_topic_name"}}" required /></div> <div class="formitem"><input form="quick_post_form" name="name" type="text" placeholder="{{lang "create_topic_name"}}" required></div>
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a>{{lang "create_topic_content"}}</a></div> <div class="formitem formlabel"><a>{{lang "create_topic_content"}}</a></div>
@ -22,7 +22,7 @@
<div class="formrow"> <div class="formrow">
<button form="quick_post_form" name="topic-button" class="formbutton">{{lang "create_topic_create_button"}}</button> <button form="quick_post_form" name="topic-button" class="formbutton">{{lang "create_topic_create_button"}}</button>
{{if .CurrentUser.Perms.UploadFiles}} {{if .CurrentUser.Perms.UploadFiles}}
<input name="quick_topic_upload_files" form="quick_post_form" id="quick_topic_upload_files" multiple type="file" style="display:none;" /> <input name="quick_topic_upload_files" form="quick_post_form" id="quick_topic_upload_files" multiple type="file" class="auto_hide">
<label for="quick_topic_upload_files" class="formbutton add_file_button">{{lang "create_topic_add_file_button"}}</label>{{end}} <label for="quick_topic_upload_files" class="formbutton add_file_button">{{lang "create_topic_add_file_button"}}</label>{{end}}
<div id="upload_file_dock"></div> <div id="upload_file_dock"></div>
</div> </div>

View File

@ -5,7 +5,7 @@
<div class="rowitem"><h1 itemprop="name">{{lang "forums_head"}}</h1></div> <div class="rowitem"><h1 itemprop="name">{{lang "forums_head"}}</h1></div>
</div> </div>
<div class="rowblock forum_list"> <div class="rowblock forum_list">
{{range .ItemList}}<div id="forum_{{.ID}}"class="rowitem{{if (.Desc) or (.LastTopic.Title)}} datarow{{end}}"itemprop="itemListElement" itemscope {{range .ItemList}}<div id="forum_{{.ID}}"class="rowitem{{if (.Desc) or (.LastTopic.Title)}} datarow{{end}}"itemprop="itemListElement"itemscope
itemtype="http://schema.org/ListItem"> itemtype="http://schema.org/ListItem">
<span class="forum_left shift_left"> <span class="forum_left shift_left">
<a href="{{.Link}}"itemprop="item">{{.Name}}</a><br> <a href="{{.Link}}"itemprop="item">{{.Name}}</a><br>

View File

@ -4,17 +4,17 @@
<div class="rowitem"><h1>{{lang "login_head"}}</h1></div> <div class="rowitem"><h1>{{lang "login_head"}}</h1></div>
</div> </div>
<div class="rowblock the_form"> <div class="rowblock the_form">
<form action="/accounts/login/submit/" method="post"> <form action="/accounts/login/submit/"method="post">
<div class="formrow login_name_row"> <div class="formrow login_name_row">
<div class="formitem formlabel"><a id="login_name_label">{{lang "login_account_name"}}</a></div> <div class="formitem formlabel"><a id="login_name_label">{{lang "login_account_name"}}</a></div>
<div class="formitem"><input name="username"type="text"placeholder="{{lang "login_account_name"}}" aria-labelledby="login_name_label" required></div> <div class="formitem"><input name="username"type="text"placeholder="{{lang "login_account_name"}}"aria-labelledby="login_name_label"required></div>
</div> </div>
<div class="formrow login_password_row"> <div class="formrow login_password_row">
<div class="formitem formlabel"><a id="login_password_label">{{lang "login_account_password"}}</a></div> <div class="formitem formlabel"><a id="login_password_label">{{lang "login_account_password"}}</a></div>
<div class="formitem"><input name="password"type="password"autocomplete="current-password"placeholder="*****"aria-labelledby="login_password_label" required></div> <div class="formitem"><input name="password"type="password"autocomplete="current-password"placeholder="*****"aria-labelledby="login_password_label" required></div>
</div> </div>
<div class="formrow login_button_row form_button_row"> <div class="formrow login_button_row form_button_row">
<div class="formitem"><button name="login-button" class="formbutton">{{lang "login_submit_button"}}</button></div> <div class="formitem"><button name="login-button"class="formbutton">{{lang "login_submit_button"}}</button></div>
<div class="formitem dont_have_account"> <div class="formitem dont_have_account">
<a href="/accounts/create/">{{lang "login_no_account"}} <a href="/accounts/create/">{{lang "login_no_account"}}
</div> </div>

View File

@ -1,12 +1,12 @@
<div class="colstack_item colstack_head"> <div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_logs_reg_head"}}</h1></div> <div class="rowitem"><h1>{{lang "panel_logs_reg_head"}}</h1></div>
</div> </div>
<div id="panel_reglogs" class="colstack_item rowlist loglist"> <div id="panel_reglogs"class="colstack_item rowlist loglist">
{{range .Logs}} {{range .Logs}}
<div class="rowitem panel_compactrow{{if not .Success}} bg_red{{end}}"> <div class="rowitem panel_compactrow{{if not .Success}} bg_red{{end}}">
<span>{{if not .Success}}{{lang "panel_logs_reg_attempt"}}{{end}}{{.Username}}{{if .Email}} ({{lang "panel_logs_reg_email"}}{{.Email}}){{end}}{{if .ParsedReason}} ({{lang "panel_logs_reg_reason"}}{{.ParsedReason}}){{end}}</span> <span>{{if not .Success}}{{lang "panel_logs_reg_attempt"}}{{end}}{{.Username}}{{if .Email}} ({{lang "panel_logs_reg_email"}}{{.Email}}){{end}}{{if .ParsedReason}} ({{lang "panel_logs_reg_reason"}}{{.ParsedReason}}){{end}}</span>
<div class="logdetail"> <div class="logdetail">
{{if $.CurrentUser.Perms.ViewIPs}}<small class="to_left" title="{{.IP}}">{{.IP}}</small>{{end}} {{if $.CurrentUser.Perms.ViewIPs}}<small class="to_left"title="{{.IP}}">{{.IP}}</small>{{end}}
<span class="to_right"><small title="{{.DoneAt}}">{{.DoneAt}}</small></span> <span class="to_right"><small title="{{.DoneAt}}">{{.DoneAt}}</small></span>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</div> </div>

View File

@ -28,14 +28,14 @@
<a class="profile_menu_item">{{lang "profile.login_for_options"}}</a> <a class="profile_menu_item">{{lang "profile.login_for_options"}}</a>
</div>{{else}} </div>{{else}}
{{if .CanMessage}}<div class="rowitem passive"> {{if .CanMessage}}<div class="rowitem passive">
<a href="/user/convos/create/?with={{.ProfileOwner.ID}}" class="profile_menu_item">{{lang "profile.send_message"}}</a> <a href="/user/convos/create/?with={{.ProfileOwner.ID}}"class="profile_menu_item">{{lang "profile.send_message"}}</a>
</div>{{end}} </div>{{end}}
<!--<div class="rowitem passive"> <!--<div class="rowitem passive">
<a class="profile_menu_item">{{lang "profile.add_friend"}}</a> <a class="profile_menu_item">{{lang "profile.add_friend"}}</a>
</div>--> </div>-->
{{if (.CurrentUser.IsSuperMod) and not (.ProfileOwner.IsSuperMod)}}<div class="rowitem passive"> {{if (.CurrentUser.IsSuperMod) and not (.ProfileOwner.IsSuperMod)}}<div class="rowitem passive">
{{if .ProfileOwner.IsBanned}}<a href="/users/unban/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" class="profile_menu_item">{{lang "profile.unban"}}</a> {{if .ProfileOwner.IsBanned}}<a href="/users/unban/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}"class="profile_menu_item">{{lang "profile.unban"}}</a>
{{else}}<a href="#ban_user"class="profile_menu_item">{{lang "profile.ban"}}</a>{{end}} {{else}}<a href="#ban_user"class="profile_menu_item">{{lang "profile.ban"}}</a>{{end}}
</div> </div>
<div class="rowitem passive"> <div class="rowitem passive">
@ -58,10 +58,10 @@
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.BanUsers}} {{if .CurrentUser.Perms.BanUsers}}
<!-- TODO: Inline the display:none; CSS --> <!-- TODO: Inline the display:none; CSS -->
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display:none;"> <div id="ban_user_head"class="colstack_item colstack_head hash_hide ban_user_hash" style="display:none;">
<div class="rowitem"><h1><a>{{lang "profile.ban_user_head"}}</a></h1></div> <div class="rowitem"><h1><a>{{lang "profile.ban_user_head"}}</a></h1></div>
</div> </div>
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" method="post" style="display:none;"> <form id="ban_user_form"class="hash_hide ban_user_hash"action="/users/ban/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}"method="post"style="display:none;">
<div class="the_form"> <div class="the_form">
{{/** TODO: Put a JS duration calculator here instead of this text? **/}} {{/** TODO: Put a JS duration calculator here instead of this text? **/}}
<div class="colline">{{lang "profile.ban_user_notice"}}</div> <div class="colline">{{lang "profile.ban_user_notice"}}</div>
@ -105,7 +105,7 @@
<div id="delete_posts_head"class="colstack_item colstack_head hash_hide delete_posts_hash"style="display:none;"> <div id="delete_posts_head"class="colstack_item colstack_head hash_hide delete_posts_hash"style="display:none;">
<div class="rowitem"><h1><a>{{lang "profile.delete_posts_head"}}</a></h1></div> <div class="rowitem"><h1><a>{{lang "profile.delete_posts_head"}}</a></h1></div>
</div> </div>
<form id="delete_posts_form" class="hash_hide delete_posts_hash" action="/users/delete-posts/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" method="post" style="display:none;"> <form id="delete_posts_form"class="hash_hide delete_posts_hash"action="/users/delete-posts/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}"method="post"style="display:none;">
<div class="the_form"> <div class="the_form">
<div class="colline">{{langf "profile.delete_posts_notice" .ProfileOwner.Posts}}</div> <div class="colline">{{langf "profile.delete_posts_notice" .ProfileOwner.Posts}}</div>
<div class="colstack_item"> <div class="colstack_item">

View File

@ -4,10 +4,10 @@
<div class="rowitem"><h1>{{lang "register_head"}}</h1></div> <div class="rowitem"><h1>{{lang "register_head"}}</h1></div>
</div> </div>
<div class="rowblock the_form"> <div class="rowblock the_form">
<form action="/accounts/create/submit/" method="post"> <form action="/accounts/create/submit/"method="post">
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a id="name_label">{{lang "register_account_name"}}</a></div> <div class="formitem formlabel"><a id="name_label">{{lang "register_account_name"}}</a></div>
<div class="formitem"><input name="name"type="text"placeholder="{{lang "register_account_name"}}" aria-labelledby="name_label"required></div> <div class="formitem"><input name="name"type="text"placeholder="{{lang "register_account_name"}}"aria-labelledby="name_label"required></div>
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a id="email_label">{{if not .Something}}{{lang "register_account_email"}}{{else}}{{lang "register_account_email_optional"}}{{end}}</a></div> <div class="formitem formlabel"><a id="email_label">{{if not .Something}}{{lang "register_account_email"}}{{else}}{{lang "register_account_email_optional"}}{{end}}</a></div>