The opening posts should be properly rendered after inline edits now.

Switched out the / in the headers for a -
The template generator can now handle FieldNodes passed to templates.

Added the topic_alt_userinfo template.
Added the topic_alt_quick_reply template.
This commit is contained in:
Azareal 2018-12-28 21:13:06 +10:00
parent f4337536dc
commit a1a90ab9fd
8 changed files with 156 additions and 93 deletions

View File

@ -612,10 +612,40 @@ func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.RangeNode) {
} }
} }
// ! Temporary, we probably want something that is good with non-struct pointers too
// For compileSubSwitch and compileSubTemplate
func (c *CTemplateSet) skipStructPointers(cur reflect.Value, id string) reflect.Value {
if cur.Kind() == reflect.Ptr {
c.detail("Looping over pointer")
for cur.Kind() == reflect.Ptr {
cur = cur.Elem()
}
c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", id)
}
return cur
}
// For compileSubSwitch and compileSubTemplate
func (c *CTemplateSet) checkIfValid(cur reflect.Value, varHolder string, holdreflect reflect.Value, varBit string, multiline bool) {
if !cur.IsValid() {
c.critical("Debug Data:")
c.critical("Holdreflect:", holdreflect)
c.critical("Holdreflect.Kind():", holdreflect.Kind())
if !c.config.SuperDebug {
c.critical("cur.Kind():", cur.Kind().String())
}
c.critical("")
if !multiline {
panic(varHolder + varBit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
panic(varBit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
}
func (c *CTemplateSet) compileSubSwitch(con CContext, node *parse.CommandNode) { func (c *CTemplateSet) compileSubSwitch(con CContext, node *parse.CommandNode) {
c.dumpCall("compileSubSwitch", con, node) c.dumpCall("compileSubSwitch", con, node)
firstWord := node.Args[0] switch n := node.Args[0].(type) {
switch n := firstWord.(type) {
case *parse.FieldNode: case *parse.FieldNode:
c.detail("Field Node:", n.Ident) c.detail("Field Node:", n.Ident)
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Variable declarations are coming soon! */ /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Variable declarations are coming soon! */
@ -627,45 +657,19 @@ func (c *CTemplateSet) compileSubSwitch(con CContext, node *parse.CommandNode) {
varBit += ".(" + cur.Type().Name() + ")" varBit += ".(" + cur.Type().Name() + ")"
} }
// ! Might not work so well for non-struct pointers
skipPointers := func(cur reflect.Value, id string) reflect.Value {
if cur.Kind() == reflect.Ptr {
c.detail("Looping over pointer")
for cur.Kind() == reflect.Ptr {
cur = cur.Elem()
}
c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", id)
}
return cur
}
var assLines string var assLines string
var multiline = false var multiline = false
for _, id := range n.Ident { for _, id := range n.Ident {
c.detail("Data Kind:", cur.Kind().String()) c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", id) c.detail("Field Bit:", id)
cur = skipPointers(cur, id) cur = c.skipStructPointers(cur, id)
c.checkIfValid(cur, con.VarHolder, con.HoldReflect, varBit, multiline)
if !cur.IsValid() {
c.error("Debug Data:")
c.error("Holdreflect:", con.HoldReflect)
c.error("Holdreflect.Kind():", con.HoldReflect.Kind())
if !c.config.SuperDebug {
c.error("cur.Kind():", cur.Kind().String())
}
c.error("")
if !multiline {
panic(con.VarHolder + varBit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
panic(varBit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
c.detail("in-loop varBit: " + varBit) c.detail("in-loop varBit: " + varBit)
if cur.Kind() == reflect.Map { if cur.Kind() == reflect.Map {
cur = cur.MapIndex(reflect.ValueOf(id)) cur = cur.MapIndex(reflect.ValueOf(id))
varBit += "[\"" + id + "\"]" varBit += "[\"" + id + "\"]"
cur = skipPointers(cur, id) cur = c.skipStructPointers(cur, id)
if cur.Kind() == reflect.Struct || cur.Kind() == reflect.Interface { if cur.Kind() == reflect.Struct || cur.Kind() == reflect.Interface {
// TODO: Move the newVarByte declaration to the top level or to the if level, if a dispInt is only used in a particular if statement // TODO: Move the newVarByte declaration to the top level or to the if level, if a dispInt is only used in a particular if statement
@ -1385,15 +1389,60 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
con.TemplateName = fname con.TemplateName = fname
if node.Pipe != nil { if node.Pipe != nil {
for _, cmd := range node.Pipe.Cmds { for _, cmd := range node.Pipe.Cmds {
firstWord := cmd.Args[0] switch p := cmd.Args[0].(type) {
switch firstWord.(type) { case *parse.FieldNode:
// TODO: Incomplete but it should cover the basics
cur := pcon.HoldReflect
var varBit string
if cur.Kind() == reflect.Interface {
cur = cur.Elem()
varBit += ".(" + cur.Type().Name() + ")"
}
for _, id := range p.Ident {
c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", id)
cur = c.skipStructPointers(cur, id)
c.checkIfValid(cur, pcon.VarHolder, pcon.HoldReflect, varBit, false)
if cur.Kind() != reflect.Interface {
cur = cur.FieldByName(id)
varBit += "." + id
}
// TODO: Handle deeply nested pointers mixed with interfaces mixed with pointers better
if cur.Kind() == reflect.Interface {
cur = cur.Elem()
varBit += ".("
// TODO: Surely, there's a better way of doing this?
if cur.Type().PkgPath() != "main" && cur.Type().PkgPath() != "" {
c.importMap["html/template"] = "html/template"
varBit += strings.TrimPrefix(cur.Type().PkgPath(), "html/") + "."
}
varBit += cur.Type().Name() + ")"
}
}
con.VarHolder = pcon.VarHolder + varBit
con.HoldReflect = cur
case *parse.DotNode: case *parse.DotNode:
con.VarHolder = pcon.VarHolder con.VarHolder = pcon.VarHolder
con.HoldReflect = pcon.HoldReflect con.HoldReflect = pcon.HoldReflect
case *parse.NilNode: case *parse.NilNode:
panic("Nil is not a command x.x") panic("Nil is not a command x.x")
default: default:
c.detail("Unknown Node: ", firstWord) c.critical("Unknown Param Type:", p)
pvar := reflect.ValueOf(p)
c.critical("param kind:", pvar.Kind().String())
c.critical("param type:", pvar.Type().Name())
if pvar.Kind() == reflect.Ptr {
c.critical("Looping over pointer")
for pvar.Kind() == reflect.Ptr {
pvar = pvar.Elem()
}
c.critical("concrete kind:", pvar.Kind().String())
c.critical("concrete type:", pvar.Type().Name())
}
panic("") panic("")
} }
} }
@ -1527,3 +1576,7 @@ func (c *CTemplateSet) error(args ...interface{}) {
log.Println(args...) log.Println(args...)
} }
} }
func (c *CTemplateSet) critical(args ...interface{}) {
log.Println(args...)
}

View File

@ -388,17 +388,19 @@ function mainInit(){
$('.show_on_edit').removeClass("edit_opened"); $('.show_on_edit').removeClass("edit_opened");
runHook("close_edit"); runHook("close_edit");
let formAction = this.form.getAttribute("action");
$.ajax({ $.ajax({
url: formAction, url: this.form.getAttribute("action"),
type: "POST", type: "POST",
dataType: "json", dataType: "json",
error: ajaxError,
data: { data: {
topic_name: topicNameInput, topic_name: topicNameInput,
topic_status: topicStatusInput, topic_status: topicStatusInput,
topic_content: topicContentInput, topic_content: topicContentInput,
topic_js: 1 js: 1
},
error: ajaxError,
success: (data,status,xhr) => {
if("Content" in data) $(".topic_content").html(data["Content"]);
} }
}); });
}); });

View File

@ -616,10 +616,22 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, s
return common.InternalErrorJSQ(err, w, r, isJs) return common.InternalErrorJSQ(err, w, r, isJs)
} }
// TODO: Avoid the load to get this faster?
topic, err = common.Topics.Get(topic.ID)
if err == sql.ErrNoRows {
return common.PreErrorJSQ("The updated topic doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs { if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
} else { } else {
_, _ = w.Write(successJSONBytes) outBytes, err := json.Marshal(JsonReply{common.ParseMessage(topic.Content, topic.ParentID, "forums")})
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
w.Write(outBytes)
} }
return nil return nil
} }

View File

@ -14,7 +14,7 @@
<form action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"> <form action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post">
<div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}"> <div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}">
<h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1> <h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1>
<span class="topic_name_forum_sep hide_on_edit"> / </span> <span class="topic_name_forum_sep hide_on_edit"> - </span>
<a href="{{.Forum.Link}}" class="topic_forum hide_on_edit">{{.Forum.Name}}</a> <a href="{{.Forum.Link}}" class="topic_forum hide_on_edit">{{.Forum.Name}}</a>
{{/** TODO: Does this need to be guarded by a permission? It's only visible in edit mode anyway, which can't be triggered, if they don't have the permission **/}} {{/** TODO: Does this need to be guarded by a permission? It's only visible in edit mode anyway, which can't be triggered, if they don't have the permission **/}}
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Loggedin}}
@ -68,13 +68,7 @@
</article> </article>
{{end}} {{end}}
<article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item top_post" aria-label="{{lang "topic.opening_post_aria"}}"> <article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item top_post" aria-label="{{lang "topic.opening_post_aria"}}">
<div class="userinfo" aria-label="{{lang "topic.userinfo_aria"}}"> {{template "topic_alt_userinfo.html" .Topic }}
<div class="avatar_item" style="background-image: url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.Topic.UserLink}}" class="the_name" rel="author">{{.Topic.CreatedByName}}</a>
{{if .Topic.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Topic.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Topic.Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
</div>
<div class="content_container"> <div class="content_container">
<div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div> <div class="hide_on_edit topic_content user_content" itemprop="text">{{.Topic.ContentHTML}}</div>
{{if .CurrentUser.Loggedin}}{{if .CurrentUser.Perms.EditTopic}}<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea> {{if .CurrentUser.Loggedin}}{{if .CurrentUser.Perms.EditTopic}}<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
@ -131,44 +125,7 @@
{{if .CurrentUser.Loggedin}} {{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.CreateReply}} {{if .CurrentUser.Perms.CreateReply}}
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}} {{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
<div class="rowblock topic_reply_container"> {{template "topic_alt_quick_reply.html" . }}
<div class="userinfo" aria-label="{{lang "topic.your_information"}}">
<div class="avatar_item" style="background-image: url({{.CurrentUser.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a>
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
</div>
<div class="rowblock topic_reply_form quick_create_form" aria-label="{{lang "topic.reply_aria"}}">
<form id="quick_post_form" enctype="multipart/form-data" action="/reply/create/?session={{.CurrentUser.Session}}" method="post"></form>
<input form="quick_post_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
<input form="quick_post_form" id="has_poll_input" name="has_poll" value="0" type="hidden" />
<div class="formrow real_first_child">
<div class="formitem">
<textarea id="input_content" form="quick_post_form" name="reply-content" placeholder="{{lang "topic.reply_content_alt"}}" required></textarea>
</div>
</div>
<div class="formrow poll_content_row auto_hide">
<div class="formitem">
<div class="pollinput" data-pollinput="0">
<input type="checkbox" disabled />
<label class="pollinputlabel"></label>
<input form="quick_post_form" name="pollinputitem[0]" class="pollinputinput" type="text" placeholder="{{lang "topic.reply_add_poll_option"}}" />
</div>
</div>
</div>
<div class="formrow quick_button_row">
<div class="formitem">
<button form="quick_post_form" name="reply-button" class="formbutton">{{lang "topic.reply_button"}}</button>
<button form="quick_post_form" class="formbutton" id="add_poll_button">{{lang "topic.reply_add_poll_button"}}</button>
{{if .CurrentUser.Perms.UploadFiles}}
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">{{lang "topic.reply_add_file_button"}}</label>
<div id="upload_file_dock"></div>{{end}}
</div>
</div>
</div>
</div>
{{end}} {{end}}
{{end}} {{end}}
{{end}} {{end}}

View File

@ -1,11 +1,5 @@
{{range .ItemList}}<article {{scope "post"}} id="post-{{.ID}}" itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item{{if .ActionType}} action_item{{end}}"> {{range .ItemList}}<article {{scope "post"}} id="post-{{.ID}}" itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item{{if .ActionType}} action_item{{end}}">
<div class="userinfo" aria-label="{{lang "topic.userinfo_aria"}}"> {{template "topic_alt_userinfo.html" . }}
<div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a>
{{if .Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
</div>
<div class="content_container"{{if .ActionType}} style="margin-left: 0px;"{{end}}> <div class="content_container"{{if .ActionType}} style="margin-left: 0px;"{{end}}>
{{if .ActionType}} {{if .ActionType}}
<span class="action_icon" style="font-size: 18px;padding-right: 5px;" aria-hidden="true">{{.ActionIcon}}</span> <span class="action_icon" style="font-size: 18px;padding-right: 5px;" aria-hidden="true">{{.ActionIcon}}</span>

View File

@ -0,0 +1,38 @@
<div class="rowblock topic_reply_container">
<div class="userinfo" aria-label="{{lang "topic.your_information"}}">
<div class="avatar_item" style="background-image: url({{.CurrentUser.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a>
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
</div>
<div class="rowblock topic_reply_form quick_create_form" aria-label="{{lang "topic.reply_aria"}}">
<form id="quick_post_form" enctype="multipart/form-data" action="/reply/create/?session={{.CurrentUser.Session}}" method="post"></form>
<input form="quick_post_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
<input form="quick_post_form" id="has_poll_input" name="has_poll" value="0" type="hidden" />
<div class="formrow real_first_child">
<div class="formitem">
<textarea id="input_content" form="quick_post_form" name="reply-content" placeholder="{{lang "topic.reply_content_alt"}}" required></textarea>
</div>
</div>
<div class="formrow poll_content_row auto_hide">
<div class="formitem">
<div class="pollinput" data-pollinput="0">
<input type="checkbox" disabled />
<label class="pollinputlabel"></label>
<input form="quick_post_form" name="pollinputitem[0]" class="pollinputinput" type="text" placeholder="{{lang "topic.reply_add_poll_option"}}" />
</div>
</div>
</div>
<div class="formrow quick_button_row">
<div class="formitem">
<button form="quick_post_form" name="reply-button" class="formbutton">{{lang "topic.reply_button"}}</button>
<button form="quick_post_form" class="formbutton" id="add_poll_button">{{lang "topic.reply_add_poll_button"}}</button>
{{if .CurrentUser.Perms.UploadFiles}}
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">{{lang "topic.reply_add_file_button"}}</label>
<div id="upload_file_dock"></div>{{end}}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,7 @@
<div class="userinfo" aria-label="{{lang "topic.userinfo_aria"}}">
<div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<div class="user_meta">
<a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a>
{{if .Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
</div>

View File

@ -8,7 +8,7 @@
<div class="optbox"> <div class="optbox">
{{if .ForumList}} {{if .ForumList}}
<div class="opt filter_opt"> <div class="opt filter_opt">
<a class="filter_opt_sep"> / </a> <a class="filter_opt_sep"> - </a>
<a href="#" class="filter_opt_label link_label" data-for="topic_list_filter_select">{{if eq .Sort.SortBy "mostviewed" }}{{lang "topic_list.most_viewed_filter"}}{{else}}{{lang "topic_list.most_recent_filter"}}{{end}} <span class="filter_opt_pointy"></span></a> <a href="#" class="filter_opt_label link_label" data-for="topic_list_filter_select">{{if eq .Sort.SortBy "mostviewed" }}{{lang "topic_list.most_viewed_filter"}}{{else}}{{lang "topic_list.most_recent_filter"}}{{end}} <span class="filter_opt_pointy"></span></a>
<div id="topic_list_filter_select" class="link_select"> <div id="topic_list_filter_select" class="link_select">
<div class="link_option link_selected"> <div class="link_option link_selected">