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:
Azareal 2019-04-10 17:40:47 +10:00
parent d5acb92aef
commit 5808f1d0ba
20 changed files with 160 additions and 104 deletions

View File

@ -277,6 +277,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"originTable", "varchar", 200, false, false, "replies"},
tblColumn{"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key
tblColumn{"path", "varchar", 200, false, false, ""},
//tblColumn{"extra", "varchar", 200, false, false, ""},
},
[]tblKey{
tblKey{"attachID", "primary"},

View File

@ -81,7 +81,7 @@ func (store *DefaultAttachmentStore) BulkMiniGetList(originTable string, ids []i
}
if len(ids) == 1 {
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)

View File

@ -88,7 +88,8 @@ var hookTable = &HookTable{
"action_end_edit_reply": nil,
"action_end_delete_reply": nil,
"router_pre_route": nil,
"router_after_filters": nil,
"router_pre_route": nil,
},
map[string][]func(string) string{
"preparse_preassign": nil,

View File

@ -9,13 +9,14 @@ import (
"unicode/utf8"
)
// TODO: Somehow localise these?
var SpaceGap = []byte(" ")
var httpProtBytes = []byte("http://")
var InvalidURL = []byte("<span style='color: red;'>[Invalid URL]</span>")
var InvalidTopic = []byte("<span style='color: red;'>[Invalid Topic]</span>")
var InvalidProfile = []byte("<span style='color: red;'>[Invalid Profile]</span>")
var InvalidForum = []byte("<span style='color: red;'>[Invalid Forum]</span>")
var unknownMedia = []byte("<span style='color: red;'>[Unknown Media]</span>")
var InvalidURL = []byte("<red>[Invalid URL]</red>")
var InvalidTopic = []byte("<red>[Invalid Topic]</red>")
var InvalidProfile = []byte("<red>[Invalid Profile]</red>")
var InvalidForum = []byte("<red>[Invalid Forum]</red>")
var unknownMedia = []byte("<red>[Unknown Media]</red>")
var URLOpen = []byte("<a href='")
var URLOpen2 = []byte("'>")
var bytesSinglequote = []byte("'")

View File

@ -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]
}
// 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" {
h := w.Header()
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
// TODO: Set the content policy header
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}
}
// TODO: Use the same hook table as downstream
hTbl := common.GetHookTable()
skip, ferr := hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
skip, ferr = hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
if skip || ferr != nil {
r.handleError(ferr,w,req,user)
}

View File

@ -597,6 +597,7 @@
"topic.poll_vote":"Vote",
"topic.poll_results":"Results",
"topic.poll_cancel":"Cancel",
"topic.poll_no_results":"No one has voted yet.",
"topic.post_controls_aria":"Controls and Author Information",
"topic.unlike_tooltip":"Unlike",
"topic.unlike_aria":"Unlike this topic",

View File

@ -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("#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("#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("@0", "<span style='color: red;'>[Invalid Profile]</span>")
msgList.Add("@-1", "<span style='color: red;'>[Invalid Profile]</span>1")
msgList.Add("@0", "<red>[Invalid Profile]</red>")
msgList.Add("@-1", "<red>[Invalid Profile]</red>1")
for _, item := range msgList.Items {
res := common.ParseMessage(item.Msg, 1, "forums")

View File

@ -32,9 +32,9 @@ func init() {
func initBbcode(plugin *common.Plugin) error {
plugin.AddHook("parse_assign", bbcodeFullParse)
bbcodeInvalidNumber = []byte("<span style='color: red;'>[Invalid Number]</span>")
bbcodeNoNegative = []byte("<span style='color: red;'>[No Negative Numbers]</span>")
bbcodeMissingTag = []byte("<span style='color: red;'>[Missing Tag]</span>")
bbcodeInvalidNumber = []byte("<red>[Invalid Number]</red>")
bbcodeNoNegative = []byte("<red>[No Negative Numbers]</red>")
bbcodeMissingTag = []byte("<red>[Missing Tag]</red>")
bbcodeBold = regexp.MustCompile(`(?s)\[b\](.*)\[/b\]`)
bbcodeItalic = regexp.MustCompile(`(?s)\[i\](.*)\[/i\]`)

View File

@ -29,7 +29,7 @@ func init() {
func initMarkdown(plugin *common.Plugin) error {
plugin.AddHook("parse_assign", markdownParse)
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
markdownUnclosedElement = []byte("<red>[Unclosed Element]</red>")
markdownBoldTagOpen = []byte("<b>")
markdownBoldTagClose = []byte("</b>")
@ -63,7 +63,7 @@ func markdownParse(msg string) string {
// Under Construction!
func _markdownParse(msg string, n int) string {
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

View File

@ -87,7 +87,7 @@ func TestBBCodeRender(t *testing.T) {
var expects string
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -96,7 +96,7 @@ func TestBBCodeRender(t *testing.T) {
}
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -105,7 +105,7 @@ func TestBBCodeRender(t *testing.T) {
}
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -114,7 +114,7 @@ func TestBBCodeRender(t *testing.T) {
}
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -123,7 +123,7 @@ func TestBBCodeRender(t *testing.T) {
}
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -132,7 +132,7 @@ func TestBBCodeRender(t *testing.T) {
}
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -141,7 +141,7 @@ func TestBBCodeRender(t *testing.T) {
}
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 + "'")
res = bbcodeFullParse(msg)
if res != expects {
@ -191,7 +191,7 @@ func TestBBCodeRender(t *testing.T) {
t.Log("Testing string '" + msg + "'")
res = bbcodeFullParse(msg)
_, 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("Expected a number between 0 and 18446744073709551615")
}
@ -199,7 +199,7 @@ func TestBBCodeRender(t *testing.T) {
t.Log("Testing string '" + msg + "'")
res = bbcodeFullParse(msg)
_, 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("Expected a number between 0 and 170141183460469231731687303715884105727")
}

View File

@ -1170,13 +1170,24 @@ function mainInit(){
//id="poll_results_{{.Poll.ID}}" class="poll_results auto_hide"
$(".poll_results_button").click(function(){
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");
fetch("/poll/results/" + pollID, {
credentials: 'same-origin'
}).then((response) => response.text()).catch((error) => console.error("Error:",error)).then((rawData) => {
// TODO: Make sure the received data is actually a list of integers
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("series: ", data);
Chartist.Pie('#poll_results_chart_' + pollID, {

View File

@ -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]
}
// 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" {
h := w.Header()
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
// TODO: Set the content policy header
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}
}
// TODO: Use the same hook table as downstream
hTbl := common.GetHookTable()
skip, ferr := hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
skip, ferr = hTbl.VhookSkippable("router_pre_route", w, req, user, prefix, extraData)
if skip || ferr != nil {
r.handleError(ferr,w,req,user)
}

View File

@ -1,6 +1,5 @@
{{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}}" />
<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>
</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 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 **/}}
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
{{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"}}" />
<button form='edit_topic_form' name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
{{end}}
@ -27,29 +27,7 @@
</div>
</div>
{{if .Poll.ID}}
<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" 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>
{{template "topic_poll.html" . }}
{{end}}
<article {{scope "opening_post"}} itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="{{lang "topic.opening_post_aria"}}">
@ -127,4 +105,4 @@
</main>
{{template "footer.html" . }}
{{template "footer.html" . }}

View File

@ -6,30 +6,29 @@
{{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}}
<main id="topicPage">
<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"}}">
<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}}">
<h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1>
<span class="topic_name_forum_sep hide_on_edit"> - </span>
<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 **/}}
{{if .CurrentUser.Loggedin}}
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
{{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"}}" />
<button name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
{{end}}
{{end}}
{{end}}
<span class="topic_view_count hide_on_edit">{{.Topic.ViewCount}}</span>
{{/** 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;">&#x1F512;&#xFE0E</span>{{end}}
</div>
</form>
<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>
<span class="topic_name_forum_sep hide_on_edit"> - </span>
<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 **/}}
{{if .CurrentUser.Loggedin}}
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
{{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"}}" />
<button form="edit_topic_form" name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
{{end}}
{{end}}
{{end}}
<span class="topic_view_count hide_on_edit">{{.Topic.ViewCount}}</span>
{{/** 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;">&#x1F512;&#xFE0E</span>{{end}}
</div>
</div>
<div class="rowblock post_container">
@ -56,7 +55,9 @@
</div>
</div>
<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>
</article>
{{end}}

View File

@ -11,20 +11,20 @@
{{if $.CurrentUser.Perms.EditReply}}
{{if .Attachments}}<div class="show_on_edit show_on_block_edit attach_edit_bay" type="reply" id="{{.ID}}">
{{range .Attachments}}
<div class="attach_item{{if .Image}} attach_image_holder{{end}}">
{{if .Image}}<img src="//{{$.Header.Site.URL}}/attachs/{{.Path}}?sectionID={{.SectionID}}&amp;sectionType=forums" height=24 width=24 />{{end}}
<span class="attach_item_path" aid="{{.ID}}" fullPath="//{{$.Header.Site.URL}}/attachs/{{.Path}}">{{.Path}}</span>
<button class="attach_item_select">{{lang "topic.select_button_text"}}</button>
<button class="attach_item_copy">{{lang "topic.copy_button_text"}}</button>
</div>
{{end}}
<div class="attach_item attach_item_buttons">
{{if $.CurrentUser.Perms.UploadFiles}}
<input name="upload_files" class="upload_files_post" id="upload_files_post_{{.ID}}" multiple type="file" style="display: none;" />
<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>
</div>
{{range .Attachments}}
<div class="attach_item{{if .Image}} attach_image_holder{{end}}">
{{if .Image}}<img src="//{{$.Header.Site.URL}}/attachs/{{.Path}}?sectionID={{.SectionID}}&amp;sectionType=forums" height=24 width=24 />{{end}}
<span class="attach_item_path" aid="{{.ID}}" fullPath="//{{$.Header.Site.URL}}/attachs/{{.Path}}">{{.Path}}</span>
<button class="attach_item_select">{{lang "topic.select_button_text"}}</button>
<button class="attach_item_copy">{{lang "topic.copy_button_text"}}</button>
</div>
{{end}}
<div class="attach_item attach_item_buttons">
{{if $.CurrentUser.Perms.UploadFiles}}
<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}}
<button class="attach_item_delete">{{lang "topic.delete_button_text"}}</button>
</div>
</div>{{end}}
{{end}}{{end}}

25
templates/topic_poll.html Normal file
View File

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

View File

@ -521,10 +521,11 @@ input[type=checkbox] + label {
background-color: var(--element-background-color);
}
input[type=checkbox]:checked + label .sel {
display: inline-block;
display: block;
width: 5px;
height: 5px;
background: var(--element-border-color);
margin-top: -2px;
}
.poll_content_row {
padding-left: 20px;
@ -544,7 +545,7 @@ input[type=checkbox]:checked + label .sel {
.show_on_block_edit:not(.edit_opened),
.hide_on_block_edit.edit_opened,
.link_select:not(.link_opened) {
display: none;
display: none !important;
}
input[type=checkbox] + label.poll_option_label {
@ -552,20 +553,22 @@ input[type=checkbox] + label.poll_option_label {
height: 18px;
}
input[type=checkbox]:checked + label.poll_option_label .sel {
display: inline-block;
display: block;
width: 10px;
height: 10px;
margin-left: 3px;
margin-top: 3px;
background: var(--element-border-color);
}
/*.poll_option {
margin-bottom: 3px;
}*/
.poll_option {
padding-bottom: 5px;
display: flex;
}
.poll_option_text {
display: inline-block;
margin-left: 3px;
display: block;
margin-left: 8px;
margin-top: 1px;
font-size: 15px;
/*font-weight: bold;*/
position: relative;
top: -1px;
color: var(--light-text-color);
@ -587,6 +590,9 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
margin-left: 16px;
margin-top: 4px;
}
.poll_results {
margin-left: 14px;
}
.formbutton {
margin-top: 12px;
@ -1078,6 +1084,9 @@ blockquote:first-child {
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
font-weight: bold;
}
red {
color: red;
}
.button_container {
margin-top: auto;
display: flex;

View File

@ -868,6 +868,9 @@ blockquote:first-child {
width: 100% !important;
padding: 16px;
}
red {
color: red;
}
.user_content.in_edit a {
display: flex;
background-color: #444444;
@ -942,6 +945,9 @@ input[type=checkbox]:checked + label .sel {
.poll_buttons button {
margin-right: 8px;
}
.poll_results {
margin-left: 12px;
}
.ip_item {
display: none;

View File

@ -280,6 +280,9 @@ h1, h2, h3, h4, h5 {
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
font-weight: bold;
}
red {
color: red;
}
.controls {
width: 100%;
@ -682,6 +685,9 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
padding-bottom: 6px;
font-size: 13px;
}
.poll_buttons > *:not(:first-child) {
margin-left: 5px;
}
.poll_results {
margin-left: auto;
max-height: 120px;

View File

@ -689,6 +689,9 @@ button.username {
.user_content strong h2, .user_content strong h3, .user_content strong h4 {
font-weight: bold;
}
red {
color: red;
}
.user_tag {
float: right;
@ -859,6 +862,9 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
padding-bottom: 3px;
border: 1px solid hsl(0, 0%, 70%);
}
.poll_buttons > *:not(:first-child) {
margin-left: 5px;
}
.poll_results {
margin-left: auto;
}