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:
parent
67772f2f9a
commit
07d478179d
|
@ -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) {
|
||||
|
@ -46,11 +64,16 @@ import ({{range .Imports}}
|
|||
)
|
||||
{{range .Hooks}}
|
||||
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 .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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
106
common/extend.go
106
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,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?
|
||||
func (t *HookTable) Hook(name string, data interface{}) interface{} {
|
||||
hooks, ok := t.Hooks[name]
|
||||
if ok {
|
||||
for _, hook := range hooks {
|
||||
/*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 {
|
||||
func (t *HookTable) HookSkip(name string, data interface{}) (skip bool) {
|
||||
for _, hook := range t.HooksSkip[name] {
|
||||
if skip = hook(data); skip {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return skip
|
||||
}
|
||||
|
||||
// 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,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
|
||||
// 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 {
|
||||
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{}:
|
||||
/*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
|
||||
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:
|
||||
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{}:
|
||||
/*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
|
||||
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:
|
||||
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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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, ""},
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
<div class='alertItem'><a href="{{.Path}}"class='text'>{{.Message}}</a></div>{{end}}
|
||||
{{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}}
|
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
<div class="formrow">
|
||||
<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 class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "create_topic_content"}}</a></div>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<div class="formrow">
|
||||
<button form="quick_post_form" name="topic-button" class="formbutton">{{lang "create_topic_create_button"}}</button>
|
||||
{{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}}
|
||||
<div id="upload_file_dock"></div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue