Improved the poll UIs slightly.
Polls without any votes should look a little nicer now. .hide_on_edit and other similar classes should now override other classes on Cosora. Added <red> to avoid having to use inline styles for parser errors. Reorganised a few elements in topic.html and topic_alt.html Split topic_poll.html out of topic.html Fixed a bug where attachments were getting assigned to the wrong reply / topic. Added the router_after_filters hook. Added the topic.poll_no_results phrase. Added two parser tests.
This commit is contained in:
parent
d5acb92aef
commit
5808f1d0ba
|
@ -277,6 +277,7 @@ func createTables(adapter qgen.Adapter) error {
|
||||||
tblColumn{"originTable", "varchar", 200, false, false, "replies"},
|
tblColumn{"originTable", "varchar", 200, false, false, "replies"},
|
||||||
tblColumn{"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key
|
tblColumn{"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key
|
||||||
tblColumn{"path", "varchar", 200, false, false, ""},
|
tblColumn{"path", "varchar", 200, false, false, ""},
|
||||||
|
//tblColumn{"extra", "varchar", 200, false, false, ""},
|
||||||
},
|
},
|
||||||
[]tblKey{
|
[]tblKey{
|
||||||
tblKey{"attachID", "primary"},
|
tblKey{"attachID", "primary"},
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (store *DefaultAttachmentStore) BulkMiniGetList(originTable string, ids []i
|
||||||
}
|
}
|
||||||
if len(ids) == 1 {
|
if len(ids) == 1 {
|
||||||
res, err := store.MiniGetList(originTable, ids[0])
|
res, err := store.MiniGetList(originTable, ids[0])
|
||||||
return map[int][]*MiniAttachment{0: res}, err
|
return map[int][]*MiniAttachment{ids[0]: res}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
amap = make(map[int][]*MiniAttachment)
|
amap = make(map[int][]*MiniAttachment)
|
||||||
|
|
|
@ -88,6 +88,7 @@ var hookTable = &HookTable{
|
||||||
"action_end_edit_reply": nil,
|
"action_end_edit_reply": nil,
|
||||||
"action_end_delete_reply": nil,
|
"action_end_delete_reply": nil,
|
||||||
|
|
||||||
|
"router_after_filters": nil,
|
||||||
"router_pre_route": nil,
|
"router_pre_route": nil,
|
||||||
},
|
},
|
||||||
map[string][]func(string) string{
|
map[string][]func(string) string{
|
||||||
|
|
|
@ -9,13 +9,14 @@ import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Somehow localise these?
|
||||||
var SpaceGap = []byte(" ")
|
var SpaceGap = []byte(" ")
|
||||||
var httpProtBytes = []byte("http://")
|
var httpProtBytes = []byte("http://")
|
||||||
var InvalidURL = []byte("<span style='color: red;'>[Invalid URL]</span>")
|
var InvalidURL = []byte("<red>[Invalid URL]</red>")
|
||||||
var InvalidTopic = []byte("<span style='color: red;'>[Invalid Topic]</span>")
|
var InvalidTopic = []byte("<red>[Invalid Topic]</red>")
|
||||||
var InvalidProfile = []byte("<span style='color: red;'>[Invalid Profile]</span>")
|
var InvalidProfile = []byte("<red>[Invalid Profile]</red>")
|
||||||
var InvalidForum = []byte("<span style='color: red;'>[Invalid Forum]</span>")
|
var InvalidForum = []byte("<red>[Invalid Forum]</red>")
|
||||||
var unknownMedia = []byte("<span style='color: red;'>[Unknown Media]</span>")
|
var unknownMedia = []byte("<red>[Unknown Media]</red>")
|
||||||
var URLOpen = []byte("<a href='")
|
var URLOpen = []byte("<a href='")
|
||||||
var URLOpen2 = []byte("'>")
|
var URLOpen2 = []byte("'>")
|
||||||
var bytesSinglequote = []byte("'")
|
var bytesSinglequote = []byte("'")
|
||||||
|
|
|
@ -792,11 +792,17 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Use the same hook table as downstream
|
||||||
|
hTbl := common.GetHookTable()
|
||||||
|
skip, ferr := hTbl.VhookSkippable("router_after_filters", w, req, prefix, extraData)
|
||||||
|
if skip || ferr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if prefix != "/ws" {
|
if prefix != "/ws" {
|
||||||
h := w.Header()
|
h := w.Header()
|
||||||
h.Set("X-Frame-Options", "deny")
|
h.Set("X-Frame-Options", "deny")
|
||||||
h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing
|
h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing
|
||||||
// TODO: Set the content policy header
|
|
||||||
h.Set("X-Content-Type-Options", "nosniff")
|
h.Set("X-Content-Type-Options", "nosniff")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,9 +1000,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
w = common.GzipResponseWriter{Writer: gz, ResponseWriter: w}
|
w = common.GzipResponseWriter{Writer: gz, ResponseWriter: w}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use the same hook table as downstream
|
skip, ferr = hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
|
||||||
hTbl := common.GetHookTable()
|
|
||||||
skip, ferr := hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
|
|
||||||
if skip || ferr != nil {
|
if skip || ferr != nil {
|
||||||
r.handleError(ferr,w,req,user)
|
r.handleError(ferr,w,req,user)
|
||||||
}
|
}
|
||||||
|
|
|
@ -597,6 +597,7 @@
|
||||||
"topic.poll_vote":"Vote",
|
"topic.poll_vote":"Vote",
|
||||||
"topic.poll_results":"Results",
|
"topic.poll_results":"Results",
|
||||||
"topic.poll_cancel":"Cancel",
|
"topic.poll_cancel":"Cancel",
|
||||||
|
"topic.poll_no_results":"No one has voted yet.",
|
||||||
"topic.post_controls_aria":"Controls and Author Information",
|
"topic.post_controls_aria":"Controls and Author Information",
|
||||||
"topic.unlike_tooltip":"Unlike",
|
"topic.unlike_tooltip":"Unlike",
|
||||||
"topic.unlike_aria":"Unlike this topic",
|
"topic.unlike_aria":"Unlike this topic",
|
||||||
|
|
|
@ -143,11 +143,13 @@ func TestParser(t *testing.T) {
|
||||||
msgList.Add("//"+common.Site.URL+"\n//"+common.Site.URL, "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a><br><a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a>")
|
msgList.Add("//"+common.Site.URL+"\n//"+common.Site.URL, "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a><br><a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a>")
|
||||||
|
|
||||||
msgList.Add("#tid-1", "<a href='/topic/1'>#tid-1</a>")
|
msgList.Add("#tid-1", "<a href='/topic/1'>#tid-1</a>")
|
||||||
|
msgList.Add("#tid-0", "<red>[Invalid Topic]</red>")
|
||||||
msgList.Add("https://"+url+"/#tid-1", "<a href='https://"+url+"/#tid-1'>https://"+url+"/#tid-1</a>")
|
msgList.Add("https://"+url+"/#tid-1", "<a href='https://"+url+"/#tid-1'>https://"+url+"/#tid-1</a>")
|
||||||
msgList.Add("#fid-1", "<a href='/forum/1'>#fid-1</a>")
|
msgList.Add("#fid-1", "<a href='/forum/1'>#fid-1</a>")
|
||||||
|
msgList.Add("#fid-0", "<red>[Invalid Forum]</red>")
|
||||||
msgList.Add("@1", "<a href='/user/admin.1' class='mention'>@Admin</a>")
|
msgList.Add("@1", "<a href='/user/admin.1' class='mention'>@Admin</a>")
|
||||||
msgList.Add("@0", "<span style='color: red;'>[Invalid Profile]</span>")
|
msgList.Add("@0", "<red>[Invalid Profile]</red>")
|
||||||
msgList.Add("@-1", "<span style='color: red;'>[Invalid Profile]</span>1")
|
msgList.Add("@-1", "<red>[Invalid Profile]</red>1")
|
||||||
|
|
||||||
for _, item := range msgList.Items {
|
for _, item := range msgList.Items {
|
||||||
res := common.ParseMessage(item.Msg, 1, "forums")
|
res := common.ParseMessage(item.Msg, 1, "forums")
|
||||||
|
|
|
@ -32,9 +32,9 @@ func init() {
|
||||||
func initBbcode(plugin *common.Plugin) error {
|
func initBbcode(plugin *common.Plugin) error {
|
||||||
plugin.AddHook("parse_assign", bbcodeFullParse)
|
plugin.AddHook("parse_assign", bbcodeFullParse)
|
||||||
|
|
||||||
bbcodeInvalidNumber = []byte("<span style='color: red;'>[Invalid Number]</span>")
|
bbcodeInvalidNumber = []byte("<red>[Invalid Number]</red>")
|
||||||
bbcodeNoNegative = []byte("<span style='color: red;'>[No Negative Numbers]</span>")
|
bbcodeNoNegative = []byte("<red>[No Negative Numbers]</red>")
|
||||||
bbcodeMissingTag = []byte("<span style='color: red;'>[Missing Tag]</span>")
|
bbcodeMissingTag = []byte("<red>[Missing Tag]</red>")
|
||||||
|
|
||||||
bbcodeBold = regexp.MustCompile(`(?s)\[b\](.*)\[/b\]`)
|
bbcodeBold = regexp.MustCompile(`(?s)\[b\](.*)\[/b\]`)
|
||||||
bbcodeItalic = regexp.MustCompile(`(?s)\[i\](.*)\[/i\]`)
|
bbcodeItalic = regexp.MustCompile(`(?s)\[i\](.*)\[/i\]`)
|
||||||
|
|
|
@ -29,7 +29,7 @@ func init() {
|
||||||
func initMarkdown(plugin *common.Plugin) error {
|
func initMarkdown(plugin *common.Plugin) error {
|
||||||
plugin.AddHook("parse_assign", markdownParse)
|
plugin.AddHook("parse_assign", markdownParse)
|
||||||
|
|
||||||
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
|
markdownUnclosedElement = []byte("<red>[Unclosed Element]</red>")
|
||||||
|
|
||||||
markdownBoldTagOpen = []byte("<b>")
|
markdownBoldTagOpen = []byte("<b>")
|
||||||
markdownBoldTagClose = []byte("</b>")
|
markdownBoldTagClose = []byte("</b>")
|
||||||
|
@ -63,7 +63,7 @@ func markdownParse(msg string) string {
|
||||||
// Under Construction!
|
// Under Construction!
|
||||||
func _markdownParse(msg string, n int) string {
|
func _markdownParse(msg string, n int) string {
|
||||||
if n > markdownMaxDepth {
|
if n > markdownMaxDepth {
|
||||||
return "<span style='color: red;'>[Markdown Error: Overflowed the max depth of 20]</span>"
|
return "<red>[Markdown Error: Overflowed the max depth of 20]</red>"
|
||||||
}
|
}
|
||||||
|
|
||||||
var outbytes []byte
|
var outbytes []byte
|
||||||
|
|
|
@ -87,7 +87,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
var expects string
|
var expects string
|
||||||
|
|
||||||
msg = "[rand][/rand]"
|
msg = "[rand][/rand]"
|
||||||
expects = "<span style='color: red;'>[Invalid Number]</span>[rand][/rand]"
|
expects = "<red>[Invalid Number]</red>[rand][/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -96,7 +96,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "[rand]-1[/rand]"
|
msg = "[rand]-1[/rand]"
|
||||||
expects = "<span style='color: red;'>[No Negative Numbers]</span>[rand]-1[/rand]"
|
expects = "<red>[No Negative Numbers]</red>[rand]-1[/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -105,7 +105,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "[rand]-01[/rand]"
|
msg = "[rand]-01[/rand]"
|
||||||
expects = "<span style='color: red;'>[No Negative Numbers]</span>[rand]-01[/rand]"
|
expects = "<red>[No Negative Numbers]</red>[rand]-01[/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -114,7 +114,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "[rand]NaN[/rand]"
|
msg = "[rand]NaN[/rand]"
|
||||||
expects = "<span style='color: red;'>[Invalid Number]</span>[rand]NaN[/rand]"
|
expects = "<red>[Invalid Number]</red>[rand]NaN[/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -123,7 +123,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "[rand]Inf[/rand]"
|
msg = "[rand]Inf[/rand]"
|
||||||
expects = "<span style='color: red;'>[Invalid Number]</span>[rand]Inf[/rand]"
|
expects = "<red>[Invalid Number]</red>[rand]Inf[/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -132,7 +132,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "[rand]+[/rand]"
|
msg = "[rand]+[/rand]"
|
||||||
expects = "<span style='color: red;'>[Invalid Number]</span>[rand]+[/rand]"
|
expects = "<red>[Invalid Number]</red>[rand]+[/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -141,7 +141,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "[rand]1+1[/rand]"
|
msg = "[rand]1+1[/rand]"
|
||||||
expects = "<span style='color: red;'>[Invalid Number]</span>[rand]1+1[/rand]"
|
expects = "<red>[Invalid Number]</red>[rand]1+1[/rand]"
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
if res != expects {
|
if res != expects {
|
||||||
|
@ -191,7 +191,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
_, err = strconv.Atoi(res)
|
_, err = strconv.Atoi(res)
|
||||||
if err != nil && res != "<span style='color: red;'>[Invalid Number]</span>[rand]18446744073709551615[/rand]" {
|
if err != nil && res != "<red>[Invalid Number]</red>[rand]18446744073709551615[/rand]" {
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
t.Error("Expected a number between 0 and 18446744073709551615")
|
t.Error("Expected a number between 0 and 18446744073709551615")
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
t.Log("Testing string '" + msg + "'")
|
t.Log("Testing string '" + msg + "'")
|
||||||
res = bbcodeFullParse(msg)
|
res = bbcodeFullParse(msg)
|
||||||
_, err = strconv.Atoi(res)
|
_, err = strconv.Atoi(res)
|
||||||
if err != nil && res != "<span style='color: red;'>[Invalid Number]</span>[rand]170141183460469231731687303715884105727[/rand]" {
|
if err != nil && res != "<red>[Invalid Number]</red>[rand]170141183460469231731687303715884105727[/rand]" {
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
t.Error("Expected a number between 0 and 170141183460469231731687303715884105727")
|
t.Error("Expected a number between 0 and 170141183460469231731687303715884105727")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1170,13 +1170,24 @@ function mainInit(){
|
||||||
//id="poll_results_{{.Poll.ID}}" class="poll_results auto_hide"
|
//id="poll_results_{{.Poll.ID}}" class="poll_results auto_hide"
|
||||||
$(".poll_results_button").click(function(){
|
$(".poll_results_button").click(function(){
|
||||||
let pollID = $(this).attr("data-poll-id");
|
let pollID = $(this).attr("data-poll-id");
|
||||||
$("#poll_results_" + pollID + " .user_content").html("<div id='poll_results_chart_"+pollID+"'></div>");
|
|
||||||
$("#poll_results_" + pollID).removeClass("auto_hide");
|
$("#poll_results_" + pollID).removeClass("auto_hide");
|
||||||
fetch("/poll/results/" + pollID, {
|
fetch("/poll/results/" + pollID, {
|
||||||
credentials: 'same-origin'
|
credentials: 'same-origin'
|
||||||
}).then((response) => response.text()).catch((error) => console.error("Error:",error)).then((rawData) => {
|
}).then((response) => response.text()).catch((error) => console.error("Error:",error)).then((rawData) => {
|
||||||
// TODO: Make sure the received data is actually a list of integers
|
// TODO: Make sure the received data is actually a list of integers
|
||||||
let data = JSON.parse(rawData);
|
let data = JSON.parse(rawData);
|
||||||
|
|
||||||
|
let allZero = true;
|
||||||
|
for(let i = 0; i < data.length; i++) {
|
||||||
|
if(data[i] != "0") allZero = false;
|
||||||
|
}
|
||||||
|
if(allZero) {
|
||||||
|
$("#poll_results_" + pollID + " .poll_no_results").removeClass("auto_hide");
|
||||||
|
console.log("all zero")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#poll_results_" + pollID + " .user_content").html("<div id='poll_results_chart_"+pollID+"'></div>");
|
||||||
console.log("rawData: ", rawData);
|
console.log("rawData: ", rawData);
|
||||||
console.log("series: ", data);
|
console.log("series: ", data);
|
||||||
Chartist.Pie('#poll_results_chart_' + pollID, {
|
Chartist.Pie('#poll_results_chart_' + pollID, {
|
||||||
|
|
|
@ -571,11 +571,17 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Use the same hook table as downstream
|
||||||
|
hTbl := common.GetHookTable()
|
||||||
|
skip, ferr := hTbl.VhookSkippable("router_after_filters", w, req, prefix, extraData)
|
||||||
|
if skip || ferr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if prefix != "/ws" {
|
if prefix != "/ws" {
|
||||||
h := w.Header()
|
h := w.Header()
|
||||||
h.Set("X-Frame-Options", "deny")
|
h.Set("X-Frame-Options", "deny")
|
||||||
h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing
|
h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing
|
||||||
// TODO: Set the content policy header
|
|
||||||
h.Set("X-Content-Type-Options", "nosniff")
|
h.Set("X-Content-Type-Options", "nosniff")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,9 +779,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
w = common.GzipResponseWriter{Writer: gz, ResponseWriter: w}
|
w = common.GzipResponseWriter{Writer: gz, ResponseWriter: w}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use the same hook table as downstream
|
skip, ferr = hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
|
||||||
hTbl := common.GetHookTable()
|
|
||||||
skip, ferr := hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
|
|
||||||
if skip || ferr != nil {
|
if skip || ferr != nil {
|
||||||
r.handleError(ferr,w,req,user)
|
r.handleError(ferr,w,req,user)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"></form>
|
|
||||||
{{if gt .Page 1}}<link rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}" />
|
{{if gt .Page 1}}<link rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}" />
|
||||||
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Topic.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
||||||
|
|
||||||
|
@ -9,9 +8,9 @@
|
||||||
<a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a>
|
<a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a>
|
||||||
</div>{{end}}
|
</div>{{end}}
|
||||||
|
|
||||||
<link rel="canonical" href="//{{.Site.URL}}{{.Topic.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
<main id="topicPage">
|
||||||
|
|
||||||
<main>
|
<link rel="canonical" href="//{{.Site.URL}}{{.Topic.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
||||||
|
|
||||||
<div {{scope "topic_title_block"}} class="rowblock rowhead topic_block" aria-label="{{lang "topic.opening_post_aria"}}">
|
<div {{scope "topic_title_block"}} class="rowblock rowhead topic_block" aria-label="{{lang "topic.opening_post_aria"}}">
|
||||||
<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}}">
|
||||||
|
@ -20,6 +19,7 @@
|
||||||
{{/** 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 not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
|
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
|
||||||
{{if .CurrentUser.Perms.EditTopic}}
|
{{if .CurrentUser.Perms.EditTopic}}
|
||||||
|
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"></form>
|
||||||
<input form='edit_topic_form' class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" aria-label="{{lang "topic.title_input_aria"}}" />
|
<input form='edit_topic_form' class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" aria-label="{{lang "topic.title_input_aria"}}" />
|
||||||
<button form='edit_topic_form' name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
|
<button form='edit_topic_form' name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -27,29 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if .Poll.ID}}
|
{{if .Poll.ID}}
|
||||||
<article class="rowblock post_container poll" aria-level="{{lang "topic.poll_aria"}}">
|
{{template "topic_poll.html" . }}
|
||||||
<div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
|
|
||||||
<div class="topic_content user_content" style="margin:0;padding:0;">
|
|
||||||
{{range .Poll.QuickOptions}}
|
|
||||||
<div class="poll_option">
|
|
||||||
<input form="poll_{{$.Poll.ID}}_form" id="poll_option_{{.ID}}" name="poll_option_input" type="checkbox" value="{{.ID}}" />
|
|
||||||
<label class="poll_option_label" for="poll_option_{{.ID}}">
|
|
||||||
<div class="sel"></div>
|
|
||||||
</label>
|
|
||||||
<span id="poll_option_text_{{.ID}}" class="poll_option_text">{{.Value}}</span>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
<div class="poll_buttons">
|
|
||||||
<button form="poll_{{.Poll.ID}}_form" class="poll_vote_button">{{lang "topic.poll_vote"}}</button>
|
|
||||||
<button class="poll_results_button" data-poll-id="{{.Poll.ID}}">{{lang "topic.poll_results"}}</button>
|
|
||||||
<a href="#"><button class="poll_cancel_button">{{lang "topic.poll_cancel"}}</button></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="poll_results_{{.Poll.ID}}" class="poll_results auto_hide">
|
|
||||||
<div class="topic_content user_content"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic.opening_post_aria"}}">
|
<article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic.opening_post_aria"}}">
|
||||||
|
|
|
@ -6,12 +6,11 @@
|
||||||
{{if ne .LastPage .Page}}<link rel="prerender next" href="{{.Topic.Link}}?page={{add .Page 1}}" />
|
{{if ne .LastPage .Page}}<link rel="prerender next" href="{{.Topic.Link}}?page={{add .Page 1}}" />
|
||||||
<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a></div>{{end}}
|
<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Topic.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a></div>{{end}}
|
||||||
|
|
||||||
|
<main id="topicPage">
|
||||||
|
|
||||||
<link rel="canonical" href="//{{.Site.URL}}{{.Topic.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
<link rel="canonical" href="//{{.Site.URL}}{{.Topic.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
||||||
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<div {{scope "topic_title_block"}} class="rowblock rowhead topic_block" aria-label="{{lang "topic.opening_post_aria"}}">
|
<div {{scope "topic_title_block"}} class="rowblock rowhead topic_block" aria-label="{{lang "topic.opening_post_aria"}}">
|
||||||
<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>
|
||||||
|
@ -20,8 +19,9 @@
|
||||||
{{if .CurrentUser.Loggedin}}
|
{{if .CurrentUser.Loggedin}}
|
||||||
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
|
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
|
||||||
{{if .CurrentUser.Perms.EditTopic}}
|
{{if .CurrentUser.Perms.EditTopic}}
|
||||||
<input class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" aria-label="{{lang "topic.title_input_aria"}}" />
|
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"></form>
|
||||||
<button name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
|
<input form="edit_topic_form" class='show_on_edit topic_name_input' name="topic_name" value='{{.Topic.Title}}' type="text" aria-label="{{lang "topic.title_input_aria"}}" />
|
||||||
|
<button form="edit_topic_form" name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -29,7 +29,6 @@
|
||||||
{{/** TODO: Inline this CSS **/}}
|
{{/** TODO: Inline this CSS **/}}
|
||||||
{{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status.closed_tooltip"}}' aria-label='{{lang "topic.status_closed_aria"}}' style="font-weight:normal;float: right;position:relative;top:-5px;">🔒︎</span>{{end}}
|
{{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status.closed_tooltip"}}' aria-label='{{lang "topic.status_closed_aria"}}' style="font-weight:normal;float: right;position:relative;top:-5px;">🔒︎</span>{{end}}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rowblock post_container">
|
<div class="rowblock post_container">
|
||||||
|
@ -56,7 +55,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="poll_results_{{.Poll.ID}}" class="content_container poll_results auto_hide">
|
<div id="poll_results_{{.Poll.ID}}" class="content_container poll_results auto_hide">
|
||||||
<div class="topic_content user_content"></div>
|
<div class="topic_content user_content">
|
||||||
|
<div class="auto_hide poll_no_results">{{lang "topic.poll_no_results"}}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="attach_item attach_item_buttons">
|
<div class="attach_item attach_item_buttons">
|
||||||
{{if $.CurrentUser.Perms.UploadFiles}}
|
{{if $.CurrentUser.Perms.UploadFiles}}
|
||||||
<input name="upload_files" class="upload_files_post" id="upload_files_post_{{.ID}}" multiple type="file" style="display: none;" />
|
<input name="upload_files" class="upload_files_post auto_hide" id="upload_files_post_{{.ID}}" multiple type="file" />
|
||||||
<label for="upload_files_post_{{.ID}}" class="formbutton add_file_button">{{lang "topic.upload_button_text"}}</label>{{end}}
|
<label for="upload_files_post_{{.ID}}" class="formbutton add_file_button">{{lang "topic.upload_button_text"}}</label>{{end}}
|
||||||
<button class="attach_item_delete">{{lang "topic.delete_button_text"}}</button>
|
<button class="attach_item_delete">{{lang "topic.delete_button_text"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<article class="rowblock post_container poll" aria-level="{{lang "topic.poll_aria"}}">
|
||||||
|
<div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
|
||||||
|
<div class="topic_content user_content">
|
||||||
|
{{range .Poll.QuickOptions}}
|
||||||
|
<div class="poll_option">
|
||||||
|
<input form="poll_{{$.Poll.ID}}_form" id="poll_option_{{.ID}}" name="poll_option_input" type="checkbox" value="{{.ID}}" />
|
||||||
|
<label class="poll_option_label" for="poll_option_{{.ID}}">
|
||||||
|
<div class="sel"></div>
|
||||||
|
</label>
|
||||||
|
<span id="poll_option_text_{{.ID}}" class="poll_option_text">{{.Value}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
<div class="poll_buttons">
|
||||||
|
<button form="poll_{{.Poll.ID}}_form" class="poll_vote_button">{{lang "topic.poll_vote"}}</button>
|
||||||
|
<button class="poll_results_button" data-poll-id="{{.Poll.ID}}">{{lang "topic.poll_results"}}</button>
|
||||||
|
<a href="#"><button class="poll_cancel_button">{{lang "topic.poll_cancel"}}</button></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="poll_results_{{.Poll.ID}}" class="poll_results auto_hide">
|
||||||
|
<div class="topic_content user_content">
|
||||||
|
<div class="auto_hide poll_no_results">{{lang "topic.poll_no_results"}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
|
@ -521,10 +521,11 @@ input[type=checkbox] + label {
|
||||||
background-color: var(--element-background-color);
|
background-color: var(--element-background-color);
|
||||||
}
|
}
|
||||||
input[type=checkbox]:checked + label .sel {
|
input[type=checkbox]:checked + label .sel {
|
||||||
display: inline-block;
|
display: block;
|
||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
background: var(--element-border-color);
|
background: var(--element-border-color);
|
||||||
|
margin-top: -2px;
|
||||||
}
|
}
|
||||||
.poll_content_row {
|
.poll_content_row {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
@ -544,7 +545,7 @@ input[type=checkbox]:checked + label .sel {
|
||||||
.show_on_block_edit:not(.edit_opened),
|
.show_on_block_edit:not(.edit_opened),
|
||||||
.hide_on_block_edit.edit_opened,
|
.hide_on_block_edit.edit_opened,
|
||||||
.link_select:not(.link_opened) {
|
.link_select:not(.link_opened) {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=checkbox] + label.poll_option_label {
|
input[type=checkbox] + label.poll_option_label {
|
||||||
|
@ -552,20 +553,22 @@ input[type=checkbox] + label.poll_option_label {
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
input[type=checkbox]:checked + label.poll_option_label .sel {
|
input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||||
display: inline-block;
|
display: block;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
|
margin-top: 3px;
|
||||||
background: var(--element-border-color);
|
background: var(--element-border-color);
|
||||||
}
|
}
|
||||||
/*.poll_option {
|
.poll_option {
|
||||||
margin-bottom: 3px;
|
padding-bottom: 5px;
|
||||||
}*/
|
display: flex;
|
||||||
|
}
|
||||||
.poll_option_text {
|
.poll_option_text {
|
||||||
display: inline-block;
|
display: block;
|
||||||
margin-left: 3px;
|
margin-left: 8px;
|
||||||
|
margin-top: 1px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
/*font-weight: bold;*/
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -1px;
|
top: -1px;
|
||||||
color: var(--light-text-color);
|
color: var(--light-text-color);
|
||||||
|
@ -587,6 +590,9 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
.poll_results {
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.formbutton {
|
.formbutton {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
@ -1078,6 +1084,9 @@ blockquote:first-child {
|
||||||
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
|
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
.button_container {
|
.button_container {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -868,6 +868,9 @@ blockquote:first-child {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
.user_content.in_edit a {
|
.user_content.in_edit a {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: #444444;
|
background-color: #444444;
|
||||||
|
@ -942,6 +945,9 @@ input[type=checkbox]:checked + label .sel {
|
||||||
.poll_buttons button {
|
.poll_buttons button {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
.poll_results {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.ip_item {
|
.ip_item {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -280,6 +280,9 @@ h1, h2, h3, h4, h5 {
|
||||||
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
|
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -682,6 +685,9 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
.poll_buttons > *:not(:first-child) {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
.poll_results {
|
.poll_results {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
max-height: 120px;
|
max-height: 120px;
|
||||||
|
|
|
@ -689,6 +689,9 @@ button.username {
|
||||||
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
|
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
.user_tag {
|
.user_tag {
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -859,6 +862,9 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
border: 1px solid hsl(0, 0%, 70%);
|
border: 1px solid hsl(0, 0%, 70%);
|
||||||
}
|
}
|
||||||
|
.poll_buttons > *:not(:first-child) {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
.poll_results {
|
.poll_results {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue