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
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))

View File

@ -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)

View File

@ -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)

View File

@ -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

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 {
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!

View File

@ -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, ""},

View File

@ -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)
}
}

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}}
<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}}

View File

@ -13,7 +13,7 @@
<div class="formitem"><textarea name="body"></textarea></div>
</div>
<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>
</form>
</div>

View File

@ -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>

View File

@ -5,7 +5,7 @@
<div class="rowitem"><h1 itemprop="name">{{lang "forums_head"}}</h1></div>
</div>
<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">
<span class="forum_left shift_left">
<a href="{{.Link}}"itemprop="item">{{.Name}}</a><br>

View File

@ -4,17 +4,17 @@
<div class="rowitem"><h1>{{lang "login_head"}}</h1></div>
</div>
<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="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 class="formrow login_password_row">
<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>
<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">
<a href="/accounts/create/">{{lang "login_no_account"}}
</div>

View File

@ -1,12 +1,12 @@
<div class="colstack_item colstack_head">
<div class="rowitem"><h1>{{lang "panel_logs_reg_head"}}</h1></div>
</div>
<div id="panel_reglogs" class="colstack_item rowlist loglist">
<div id="panel_reglogs"class="colstack_item rowlist loglist">
{{range .Logs}}
<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>
<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>
<div style="clear:both;"></div>
</div>

View File

@ -28,14 +28,14 @@
<a class="profile_menu_item">{{lang "profile.login_for_options"}}</a>
</div>{{else}}
{{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 class="rowitem passive">
<a class="profile_menu_item">{{lang "profile.add_friend"}}</a>
</div>-->
{{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}}
</div>
<div class="rowitem passive">
@ -58,10 +58,10 @@
{{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.BanUsers}}
<!-- 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>
<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">
{{/** TODO: Put a JS duration calculator here instead of this text? **/}}
<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 class="rowitem"><h1><a>{{lang "profile.delete_posts_head"}}</a></h1></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="colline">{{langf "profile.delete_posts_notice" .ProfileOwner.Posts}}</div>
<div class="colstack_item">

View File

@ -4,10 +4,10 @@
<div class="rowitem"><h1>{{lang "register_head"}}</h1></div>
</div>
<div class="rowblock the_form">
<form action="/accounts/create/submit/" method="post">
<form action="/accounts/create/submit/"method="post">
<div class="formrow">
<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 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>