From 07d478179d9fff27d784583dcd7ca23c14759c3c Mon Sep 17 00:00:00 2001 From: Azareal Date: Tue, 26 May 2020 17:53:56 +1000 Subject: [PATCH] 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 --- cmd/common_hook_gen/hookgen.go | 37 ++++++++-- cmd/hook_gen/main.go | 5 +- cmd/hook_stub_gen/main.go | 5 +- common/extend.go | 122 ++++++++++++++++++++------------- extend/guilds/lib/guilds.go | 12 ++-- extend/guilds/plugin_guilds.go | 7 +- routes/forum_list.go | 9 ++- templates/alert.html | 3 +- templates/create_convo.html | 2 +- templates/create_topic.html | 4 +- templates/forums.html | 2 +- templates/login.html | 6 +- templates/panel_reglogs.html | 4 +- templates/profile.html | 10 +-- templates/register.html | 4 +- 15 files changed, 141 insertions(+), 91 deletions(-) diff --git a/cmd/common_hook_gen/hookgen.go b/cmd/common_hook_gen/hookgen.go index da67b4f2..491b42bf 100644 --- a/cmd/common_hook_gen/hookgen.go +++ b/cmd/common_hook_gen/hookgen.go @@ -19,11 +19,15 @@ type Hook struct { Ret string Type string 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) { - 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("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_topic_list_start","w http.ResponseWriter,r *http.Request,u *User,h *Header") 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("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) { @@ -45,12 +63,17 @@ import ({{range .Imports}} "{{.}}"{{end}} ) {{range .Hooks}} -func H_{{.Name}}_hook(t *HookTable, {{.Params}}) {{.Ret}} { {{if .Any}} - hook := t.{{.Type}}["{{.Name}}"] +func H_{{.Name}}_hook(t *HookTable,{{.Params}}) {{.Ret}} { {{if .Any}} + {{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 .Ret}}return {{end}}hook({{.Params2}}) - } {{end}} - {{if .Ret}}return false, nil{{end}} + } {{end}}{{end}}{{if .Pure}} + return {{.Pure}}{{else if .Ret}} + return {{.DefaultRet}}{{end}} }{{end}} ` tmpl := template.Must(template.New("hooks").Parse(fileData)) diff --git a/cmd/hook_gen/main.go b/cmd/hook_gen/main.go index 2609fa80..a7c56bc5 100644 --- a/cmd/hook_gen/main.go +++ b/cmd/hook_gen/main.go @@ -20,7 +20,6 @@ func main() { if r := recover(); r != nil { fmt.Println(r) debug.PrintStack() - return } }() @@ -49,7 +48,7 @@ func main() { imports := []string{"net/http"} 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 first := true for _, param := range strings.Split(params,",") { @@ -60,7 +59,7 @@ func main() { params2 += pspl[0] 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) diff --git a/cmd/hook_stub_gen/main.go b/cmd/hook_stub_gen/main.go index 482850a1..d8837a7e 100644 --- a/cmd/hook_stub_gen/main.go +++ b/cmd/hook_stub_gen/main.go @@ -16,13 +16,12 @@ func main() { if r := recover(); r != nil { fmt.Println(r) debug.PrintStack() - return } }() imports := []string{"net/http"} 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 first := true for _, param := range strings.Split(params,",") { @@ -33,7 +32,7 @@ func main() { params2 += pspl[0] 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) diff --git a/common/extend.go b/common/extend.go index edf1f6af..a8d0572c 100644 --- a/common/extend.go +++ b/common/extend.go @@ -49,14 +49,16 @@ var hookTableBox atomic.Value // 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 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{} VhookSkippable_ map[string]func(...interface{}) (bool, RouteError) Sshooks map[string][]func(string) string PreRenderHooks map[string][]func(http.ResponseWriter, *http.Request, *User, interface{}) bool // For future use: - messageHooks map[string][]func(Message, PageInt, ...interface{}) interface{} + //messageHooks map[string][]func(Message, PageInt, ...interface{}) interface{} } func init() { @@ -65,15 +67,18 @@ func init() { // For extend.go use only, access this via GetHookTable() elsewhere var hookTable = &HookTable{ - map[string][]func(interface{}) interface{}{ - "forums_frow_assign": nil, - "topic_create_frow_assign": nil, + //map[string][]func(interface{}) interface{}{}, + map[string][]func(interface{}){ + "forums_frow_assign": nil, //hg + }, + map[string][]func(interface{}) bool{ + "topic_create_frow_assign": nil, //hg }, map[string]func(...interface{}) interface{}{ //"convo_post_update":nil, //"convo_post_create":nil, - "forum_trow_assign": nil, + ///"forum_trow_assign": nil, "topics_topic_row_assign": nil, //"topics_user_row_assign": nil, "topic_reply_row_assign": nil, @@ -83,8 +88,8 @@ var hookTable = &HookTable{ "router_end": nil, }, map[string]func(...interface{}) (bool, RouteError){ - "simple_forum_check_pre_perms": nil, - "forum_check_pre_perms": nil, + "simple_forum_check_pre_perms": nil, //hg + "forum_check_pre_perms": nil, //hg "route_topic_list_start": nil, "route_topic_list_mostviewed_start": nil, @@ -125,7 +130,7 @@ var hookTable = &HookTable{ "parse_assign": nil, }, nil, - nil, + //nil, } var hookTableUpdateMutex sync.Mutex @@ -146,25 +151,24 @@ 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? -func (t *HookTable) Hook(name string, data interface{}) interface{} { - hooks, ok := t.Hooks[name] - if ok { - for _, hook := range hooks { - data = hook(data) - } +/*func (t *HookTable) Hook(name string, data interface{}) interface{} { + for _, hook := range t.Hooks[name] { + data = hook(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 -func (t *HookTable) HookSkippable(name string, data interface{}) (skip bool) { - hooks, ok := t.Hooks[name] - if ok { - for _, hook := range hooks { - skip = hook(data).(bool) - if skip { - break - } +func (t *HookTable) HookSkip(name string, data interface{}) (skip bool) { + for _, hook := range t.HooksSkip[name] { + if skip = hook(data); skip { + break } } return skip @@ -173,24 +177,21 @@ func (t *HookTable) HookSkippable(name string, data interface{}) (skip bool) { // Hooks with a variable number of arguments // TODO: Use RunHook semantics to allow multiple lined up plugins / modules their turn? func (t *HookTable) Vhook(name string, data ...interface{}) interface{} { - hook := t.Vhooks[name] - if hook != nil { + if hook := t.Vhooks[name]; hook != nil { return hook(data...) } return nil } func (t *HookTable) VhookNoRet(name string, data ...interface{}) { - hook := t.Vhooks[name] - if hook != nil { + if hook := t.Vhooks[name]; hook != nil { _ = hook(data...) } } // TODO: Find a better way of doing this func (t *HookTable) VhookNeedHook(name string, data ...interface{}) (ret interface{}, hasHook bool) { - hook := t.Vhooks[name] - if hook != nil { + if hook := t.Vhooks[name]; hook != nil { return hook(data...), true } 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 func (t *HookTable) VhookSkippable(name string, data ...interface{}) (bool, RouteError) { - hook := t.VhookSkippable_[name] - if hook != nil { + if hook := t.VhookSkippable_[name]; hook != nil { return hook(data...) } return false, nil } /*func VhookSkippableTest(t *HookTable, name string, data ...interface{}) (bool, RouteError) { - hook := t.VhookSkippable_[name] - if hook != nil { + if hook := t.VhookSkippable_[name]; hook != nil { return hook(data...) } return false, nil @@ -224,11 +223,8 @@ 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 // 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 { - ssHooks, ok := t.Sshooks[name] - if ok { - for _, hook := range ssHooks { - data = hook(data) - } + for _, hook := range t.Sshooks[name] { + data = hook(data) } return data } @@ -472,12 +468,24 @@ func (pl *Plugin) AddHook(name string, hInt interface{}) { defer hookTableUpdateMutex.Unlock() switch h := hInt.(type) { - case func(interface{}) interface{}: - if len(hookTable.Hooks[name]) == 0 { - hookTable.Hooks[name] = []func(interface{}) interface{}{} + /*case func(interface{}) interface{}: + if len(hookTable.Hooks[name]) == 0 { + hookTable.Hooks[name] = []func(interface{}) interface{}{} + } + hookTable.Hooks[name] = append(hookTable.Hooks[name], h) + pl.Hooks[name] = len(hookTable.Hooks[name]) - 1*/ + case func(interface{}): + if len(hookTable.HooksNoRet[name]) == 0 { + hookTable.HooksNoRet[name] = []func(interface{}){} } - hookTable.Hooks[name] = append(hookTable.Hooks[name], h) - pl.Hooks[name] = len(hookTable.Hooks[name]) - 1 + 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: if len(hookTable.Sshooks[name]) == 0 { hookTable.Sshooks[name] = []func(string) string{} @@ -521,14 +529,30 @@ func (pl *Plugin) RemoveHook(name string, hInt interface{}) { } switch hInt.(type) { - case func(interface{}) interface{}: - hook := hookTable.Hooks[name] + /*case func(interface{}) interface{}: + hook := hookTable.Hooks[name] + if len(hook) == 1 { + hook = []func(interface{}) interface{}{} + } else { + hook = append(hook[:key], hook[key+1:]...) + } + hookTable.Hooks[name] = hook*/ + case func(interface{}): + hook := hookTable.HooksNoRet[name] if len(hook) == 1 { - hook = []func(interface{}) interface{}{} + hook = []func(interface{}){} } else { hook = append(hook[:key], hook[key+1:]...) } - hookTable.Hooks[name] = hook + 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: hook := hookTable.Sshooks[name] if len(hook) == 1 { @@ -589,8 +613,8 @@ func InitPlugins() { // ? - Are the following functions racey? func RunTaskHook(name string) error { for _, hook := range taskHooks[name] { - if err := hook(); err != nil { - return err + if e := hook(); e != nil { + return e } } return nil diff --git a/extend/guilds/lib/guilds.go b/extend/guilds/lib/guilds.go index 6fedab64..a44927d7 100644 --- a/extend/guilds/lib/guilds.go +++ b/extend/guilds/lib/guilds.go @@ -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 { - h, ferr := c.UserCheck(w, r, &user) + h, ferr := c.UserCheck(w, r, user) if ferr != nil { 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 { - h, ferr := c.UserCheck(w, r, &user) + h, ferr := c.UserCheck(w, r, user) if ferr != nil { 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} // 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 } 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) 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 { c.LogError(err) return false @@ -442,13 +442,13 @@ func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) { return true, c.InternalError(err, w, r) } else if err != nil { // 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: How does this even work? Refactor it along with the rest of this plugin! 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! diff --git a/extend/guilds/plugin_guilds.go b/extend/guilds/plugin_guilds.go index 7285d73c..1c71557e 100644 --- a/extend/guilds/plugin_guilds.go +++ b/extend/guilds/plugin_guilds.go @@ -2,7 +2,7 @@ package main import ( 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 @@ -54,7 +54,7 @@ func initGuilds(pl *c.Plugin) (err error) { return acc.FirstError() } -func deactivateGuilds(pl *common.Plugin) { +func deactivateGuilds(pl *c.Plugin) { pl.RemoveHook("intercept_build_widgets", guilds.Widgets) pl.RemoveHook("trow_assign", guilds.TrowAssign) 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 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", []tC{ tC{"guildID", "int", 0, false, true, ""}, diff --git a/routes/forum_list.go b/routes/forum_list.go index 6c6a9ce1..3f9df081 100644 --- a/routes/forum_list.go +++ b/routes/forum_list.go @@ -9,7 +9,11 @@ import ( ) 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 { 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) } } - 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) } } diff --git a/templates/alert.html b/templates/alert.html index 600dc614..899b5056 100644 --- a/templates/alert.html +++ b/templates/alert.html @@ -1,2 +1 @@ -{{if .Avatar}}
{{.Message}}
{{else}} -
{{.Message}}
{{end}} \ No newline at end of file +{{if .Avatar}}
{{.Message}}
{{else}}
{{.Message}}
{{end}} \ No newline at end of file diff --git a/templates/create_convo.html b/templates/create_convo.html index e5d5fc30..e75c5a60 100644 --- a/templates/create_convo.html +++ b/templates/create_convo.html @@ -13,7 +13,7 @@
-
+
\ No newline at end of file diff --git a/templates/create_topic.html b/templates/create_topic.html index 64b624fd..eff6ed79 100644 --- a/templates/create_topic.html +++ b/templates/create_topic.html @@ -13,7 +13,7 @@
-
+
@@ -22,7 +22,7 @@
{{if .CurrentUser.Perms.UploadFiles}} - + {{end}}
diff --git a/templates/forums.html b/templates/forums.html index 85092837..108e4d55 100644 --- a/templates/forums.html +++ b/templates/forums.html @@ -5,7 +5,7 @@

{{lang "forums_head"}}

- {{range .ItemList}}
{{.Name}}
diff --git a/templates/login.html b/templates/login.html index a24f7bc1..42b7d70e 100644 --- a/templates/login.html +++ b/templates/login.html @@ -4,17 +4,17 @@

{{lang "login_head"}}

-
+