Waline(安全) - item.html 的 path 从 window.location.href 改为剔除 ?t= 后的 URL。 私密题 /_/<domain>/<id>?t=<token> 的钥匙原本会整条发往 Waline 服务端 并落进其访问日志。只删 t、保留其他参数,公开题的评论线程键不变。 cwebp(可靠性) - ConvertGifToAnimatedWebP 改用 exec.CommandContext + 30s 上限。 原来裸 exec.Command 无超时,畸形/超大 GIF 会把子进程和上传 goroutine 一起挂住。 超时杀进程并记 Warn,调用方按既有逻辑回退为原 GIF,不影响上传成功。 投票选项长度(正确性) - vote_options.content 由 varchar(200) 放宽到 300,与配置默认值一致。 此前 201–300 字的选项通过校验后在写库阶段失败。 - 新增 db.MaxVoteOptionLen;voteOptionMaxLen() 取配置与存储上限的较小值, 校验与前端 maxlength 同源,避免配置调大后再次越界。 多选 checkbox 去重(正确性) - 抽出 appendUniqueValue,预置值与「其他」文本共用同一 seen 集合。 此前重复提交同名字段会让同一选项在统计里被计多次。 测试 - vote_dedupe_test.go:去重与顺序。 - vote_option_len_test.go:配置小于/等于/大于存储上限三种情形。 - TestVoteOptionMaxLengthRoundTrip:300 个多字节字符存回不截断。 - TestVoteOptionColumnWidens:把列改回 200 后 AutoMigrate 能扩回 300(老库升级路径)。 验证:WSL2 Debian + PostgreSQL 全量 go test ./... 27 个包全绿; 隔离实例渲染单题页确认新的 path 逻辑。
502 lines
25 KiB
HTML
502 lines
25 KiB
HTML
{{template "base/header" .}}
|
||
<link rel="shortcut icon"
|
||
href="{{.PageUser.Avatar}}?x-oss-process=image/auto-orient,1/quality,q_70/sharpen,200/resize,limit_0,m_fill,w_200,h_200"/>
|
||
<link rel="apple-touch-icon"
|
||
href="{{.PageUser.Avatar}}?x-oss-process=image/auto-orient,1/quality,q_70/sharpen,200/resize,limit_0,m_fill,w_200,h_200"/>
|
||
<link rel="icon" sizes="192x192"
|
||
href="{{.PageUser.Avatar}}?x-oss-process=image/auto-orient,1/quality,q_70/sharpen,200/resize,limit_0,m_fill,w_200,h_200">
|
||
<style>
|
||
a.uk-button.uk-button-link {
|
||
text-transform: none;
|
||
}
|
||
/* 回答行:左侧「回答」与右侧「标记为已读/标记为未读」按钮等高 */
|
||
.tb-ans-row .uk-button {
|
||
height: 40px;
|
||
line-height: 38px;
|
||
}
|
||
/* 页脚次级操作之间的分隔:返回提问箱 / 下一条未读 靠在一起会连成一片 */
|
||
.tb-foot-sep {
|
||
margin: 0 4px;
|
||
color: #b0b6bd;
|
||
}
|
||
html[data-theme="dark"] .tb-foot-sep {
|
||
color: #5c6674;
|
||
}
|
||
/* 手机端三个按钮在 300px 内放不下一行(实测需 416px):
|
||
保留原来的「回答 / 标记为已读」一行不动,让「回答并下一条未读」独占下一行整行宽度。
|
||
display:contents 把两个提交按钮提升为该行的直接子项,才能用 order 把它排到状态按钮之后。 */
|
||
@media (max-width: 640px) {
|
||
.tb-ans-actions {
|
||
display: contents;
|
||
}
|
||
.tb-ans-actions .uk-button {
|
||
order: 1;
|
||
}
|
||
.tb-ans-state {
|
||
order: 2;
|
||
margin-left: auto;
|
||
}
|
||
.tb-ans-next {
|
||
order: 3;
|
||
flex: 1 1 100%;
|
||
}
|
||
/* 上面 .tb-ans-actions .uk-button 的 order:1 特异性更高,这里必须同级别盖回去 */
|
||
.tb-ans-actions .uk-button.tb-ans-next {
|
||
order: 3;
|
||
}
|
||
}
|
||
.tb-followup-card {
|
||
border: 1px solid #e7e9ec;
|
||
border-left: 3px solid #b0b6bd;
|
||
border-radius: var(--tb-radius-sm);
|
||
padding: 12px 14px;
|
||
background: #fbfbfc;
|
||
}
|
||
html[data-theme="dark"] .tb-followup-card {
|
||
background: #1b1e23;
|
||
border-color: #2c2f36;
|
||
border-left-color: #3a4250;
|
||
}
|
||
html[data-theme="dark"] .tb-followup-card .uk-button-default {
|
||
background-color: #2c2f36;
|
||
color: #e5e9ec;
|
||
border-color: #3a4250;
|
||
}
|
||
html[data-theme="dark"] .tb-followup-card .uk-button-default:hover {
|
||
background-color: #353941;
|
||
color: #ffffff;
|
||
}
|
||
</style>
|
||
|
||
<div>
|
||
<div class="uk-card uk-card-default">
|
||
<div class="uk-card-header">
|
||
<div class="uk-text-left uk-text-small uk-text-muted">{{Date .Question.CreatedAt "Y-m-d H:i:s"}}</div>
|
||
<div class="md-body uk-text-break uk-margin-top uk-margin-bottom">{{MarkdownFormat .Question.Content}}</div>
|
||
{{ if .AskUploadImages }}
|
||
<ul class="uk-thumbnav" uk-margin>
|
||
{{ range $index, $elem := .AskUploadImages }}
|
||
<div uk-lightbox>
|
||
<a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}">
|
||
<img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt="" loading="lazy">
|
||
</a>
|
||
</div>
|
||
{{ end }}
|
||
</ul>
|
||
{{ end }}
|
||
</div>
|
||
|
||
{{if ne .Question.Answer ""}}
|
||
<div class="uk-card-body">
|
||
<!--<div class="uk-text-left uk-text-small uk-text-muted">{{Date .Question.UpdatedAt "Y-m-d H:i:s"}}</div>-->
|
||
<div id="answer-display">
|
||
<div class="md-body uk-text-break">{{MarkdownFormat .Question.Answer}}</div>
|
||
{{ if .AnswerUploadImages }}
|
||
<ul class="uk-thumbnav" uk-margin>
|
||
{{ range $index, $elem := .AnswerUploadImages }}
|
||
<div uk-lightbox>
|
||
<a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}">
|
||
<img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt="" loading="lazy">
|
||
</a>
|
||
</div>
|
||
{{ end }}
|
||
</ul>
|
||
{{ end }}
|
||
</div>
|
||
<p class="uk-text-small uk-text-right uk-text-muted">{{t .LangCode "qitem.answeredBy" .PageUser.Name (Date .Question.UpdatedAt "Y-m-d H:i:s")}}</p>
|
||
{{ if not .CanDelete }}
|
||
<!-- 举报入口:仅对非箱主显示;默认折叠,避免在每条回答下方都堆一个显眼按钮。
|
||
理由为固定枚举(不接受自由文本),重复举报服务端幂等处理。 -->
|
||
<div class="uk-margin-small-top">
|
||
<details>
|
||
<summary class="uk-text-small uk-text-muted" style="cursor: pointer;">⚑ {{t .LangCode "report.entry"}}</summary>
|
||
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/report" class="uk-margin-small-top uk-flex uk-flex-middle uk-flex-wrap" style="gap: 8px;">
|
||
{{ .CSRFTokenHTML }}
|
||
<select name="reason" class="uk-select uk-form-small" style="max-width: 200px;">
|
||
<option value="harassment">{{t .LangCode "report.reason.harassment"}}</option>
|
||
<option value="doxxing">{{t .LangCode "report.reason.doxxing"}}</option>
|
||
<option value="divisive">{{t .LangCode "report.reason.divisive"}}</option>
|
||
<option value="other">{{t .LangCode "report.reason.other"}}</option>
|
||
</select>
|
||
{{ if eq .CaptchaType "math" }}
|
||
<!-- 算数验证码:点击后由 header 的全局弹窗接管,答案回填到本表单的隐藏域 -->
|
||
<input type="hidden" name="g-recaptcha-response" value="">
|
||
<button type="button" class="uk-button uk-button-default uk-button-small math-captcha-trigger">{{t .LangCode "report.submit"}}</button>
|
||
{{ else }}
|
||
<button type="submit" class="uk-button uk-button-default uk-button-small g-recaptcha" data-sitekey="{{.RecaptchaSiteKey}}" data-callback="tbReportCaptchaOk">{{t .LangCode "report.submit"}}</button>
|
||
<script>
|
||
function tbReportCaptchaOk() {
|
||
var f = document.querySelector('form[action$="/report"]');
|
||
if (f) { f.submit(); }
|
||
}
|
||
</script>
|
||
{{ end }}
|
||
</form>
|
||
</details>
|
||
</div>
|
||
{{ end }}
|
||
<!-- 分享此提问(左)与 更新回答(右)一左一右分开;箱主未登录(无更新回答按钮)时分享按钮居中;编辑区在下方由 tbToggleAnswerEditor 控制 -->
|
||
<div class="uk-flex uk-flex-middle uk-flex-wrap uk-margin-small-top {{ if not .CanDelete }}uk-flex-center{{ end }}" style="gap: 8px;">
|
||
{{ if and (not .Question.IsPrivate) (ne .Question.Answer "") }}
|
||
<a class="uk-button uk-button-default uk-button-small" href="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/share-card" target="_blank">📤 {{t .LangCode "qitem.shareCard"}}</a>
|
||
{{ end }}
|
||
{{ if .CanDelete }}
|
||
<div id="answer-edit-trigger" style="margin-left: auto;">
|
||
<button type="button" class="uk-button uk-button-default uk-button-small" onclick="tbToggleAnswerEditor()">{{t .LangCode "qitem.updateAnswer"}}</button>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
{{ if not .CanDelete }}
|
||
<!-- 评论区资源只在本页加载(此前挂在 header 里,等于每页都下载) -->
|
||
<link rel="stylesheet" href="https://r2.337845818.best/tamabox/cdn/waline/client/v2/waline-no-commits.css" media="print" onload="this.media='all'"/>
|
||
<script defer src="https://cdnjs.snrat.com/ajax/libs/waline/2.15.8/waline.js"></script>
|
||
<div id="waline"></div>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
const waline = Waline.init({
|
||
el: '#waline',
|
||
//后端服务器地址
|
||
serverURL: 'https://waline.337845818.best/',
|
||
// 评论线程键:必须剔除 ?t= 私密题 token。
|
||
// 原来直接传 location.href,私密题 /_/<domain>/<id>?t=<token> 的钥匙
|
||
// 会整条 URL 发到 Waline 服务端并落进它的访问日志;token 本身就能读该题,
|
||
// 等于凭空多出一份可被日志检索的凭证副本。
|
||
// 只删 t、保留其余参数:公开题的 page/sort 等分页参数不变,
|
||
// 已有评论线程的键不受影响。
|
||
path: (function () {
|
||
var u = new URL(window.location.href);
|
||
u.searchParams.delete('t');
|
||
return u.toString();
|
||
})(),
|
||
// 中文默认
|
||
locale: {
|
||
nick: '昵称',
|
||
nickError: '昵称不能少于3个字符',
|
||
mail: '邮箱',
|
||
mailError: '请填写正确的邮件地址',
|
||
link: '网址',
|
||
optional: '可选',
|
||
placeholder: '欢迎评论(需要手工审核,不会立即展现)',
|
||
sofa: '来发评论吧~',
|
||
submit: '提交',
|
||
like: '喜欢',
|
||
cancelLike: '取消喜欢',
|
||
reply: '回复',
|
||
cancelReply: '取消回复',
|
||
comment: '评论',
|
||
refresh: '刷新',
|
||
more: '加载更多...',
|
||
preview: '预览',
|
||
emoji: '表情',
|
||
uploadImage: '上传图片',
|
||
seconds: '秒前',
|
||
minutes: '分钟前',
|
||
hours: '小时前',
|
||
days: '天前',
|
||
now: '刚刚',
|
||
uploading: '正在上传',
|
||
login: '登录',
|
||
logout: '退出',
|
||
admin: '博主',
|
||
sticky: '置顶',
|
||
word: '字',
|
||
wordHint: '评论字数应在 $0 到 $1 字之间!\n当前字数:$2',
|
||
anonymous: '无名小狼',
|
||
level0: '潜水',
|
||
level1: '冒泡',
|
||
level2: '吐槽',
|
||
level3: '活跃',
|
||
level4: '话痨',
|
||
level5: '传说',
|
||
gif: '表情包',
|
||
gifSearchPlaceholder: '搜索表情包',
|
||
profile: '个人资料',
|
||
approved: '通过',
|
||
waiting: '待审核',
|
||
spam: '垃圾',
|
||
unsticky: '取消置顶',
|
||
oldest: '按倒序',
|
||
latest: '按正序',
|
||
hottest: '按热度',
|
||
reactionTitle: '',
|
||
reaction0: '哇哦',
|
||
reaction1: 'prpr',
|
||
reaction2: '流汗',
|
||
reaction3: '生气',
|
||
reaction4: '流鼻血',
|
||
reaction5: 'あべ',
|
||
},
|
||
reaction: [
|
||
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGQ3OTRhYzRjMTBkZmZhZDVmZTA1EIjM1y8.gif',
|
||
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChQwOGEyOTlhYzRjMTBmOWFkZjAwYRCIzNcv.gif',
|
||
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGRhOTVhYzRjMTBkNmViYTVlNTA2EIjM1y8.gif',
|
||
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGU1OTJhYzRjMTBmMzk3ZmVmNTAyEIjM1y8.gif',
|
||
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGNkODdhYzRjMTBjYmEwOTFjNjAxEIjM1y8.gif',
|
||
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChQwODliOGVhYzRjMTBmOGM0ZWY1ZRCIzNcv.gif',
|
||
],
|
||
meta: ['nick','mail'],
|
||
requiredMeta: ['mail'],
|
||
pageview: true,
|
||
search: false,
|
||
comment: false,
|
||
login: 'disable',
|
||
wordLimit: 50,
|
||
pageSize: 5,
|
||
avatar: 'monsterid',
|
||
emoji: [
|
||
'https://gcore.jsdelivr.net/gh/tamakyi/emoji-collection@main/acfun-color/60x60',
|
||
],
|
||
//禁止图片上传按钮
|
||
imageUploader: false,
|
||
//不显示版权标志
|
||
copyright: false,
|
||
});
|
||
});
|
||
</script>
|
||
{{ end }}
|
||
{{ if .CanDelete }}
|
||
<div id="answer-editor" style="display:none;">
|
||
<hr>
|
||
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/answer" enctype="multipart/form-data">
|
||
{{ .CSRFTokenHTML }}
|
||
<div class="uk-margin uk-text-center">
|
||
<textarea name="answer" class="uk-textarea" rows="5" maxlength="{{ .Limits.AnswerContentLen }}"
|
||
placeholder="{{t .LangCode "qitem.answerPlaceholder"}}">{{ .Question.Answer }}</textarea>
|
||
<div x-data="{ files: null }" class="tb-img-uploader">
|
||
<div uk-form-custom>
|
||
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
|
||
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
|
||
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
|
||
</div>
|
||
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
|
||
@click="files = null"></button>
|
||
</div>
|
||
</div>
|
||
{{ if ne .Question.ReceiveReplyEmail "" }}
|
||
<div class="uk-alert-warning uk-text-small" uk-alert>
|
||
<p>{{t .LangCode "qitem.emailNotifyHint"}}</p>
|
||
</div>
|
||
{{ end }}
|
||
<div class="uk-margin uk-text-center">
|
||
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t .LangCode "qitem.updateAnswer"}}</button>
|
||
<button type="button" class="uk-button uk-button-default uk-button-small uk-margin-small-left" onclick="tbToggleAnswerEditor()">{{t .LangCode "common.cancel"}}</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
{{ else if .CanDelete }}
|
||
<div class="uk-card-body uk-padding-remove-top">
|
||
<hr>
|
||
<h5 class="uk-text-center">{{t .LangCode "qitem.answerTitle"}}</h5>
|
||
<form id="tb-answer-form" method="post" action="{{ if .AnswerAction }}{{ .AnswerAction }}{{ else }}/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/answer{{ end }}" enctype="multipart/form-data">
|
||
{{ .CSRFTokenHTML }}
|
||
<div class="uk-margin uk-text-center">
|
||
<textarea name="answer" class="uk-textarea" rows="5" maxlength="{{ .Limits.AnswerContentLen }}"
|
||
placeholder="{{t .LangCode "qitem.answerPlaceholder"}}">{{ .answer }}</textarea>
|
||
<div x-data="{ files: null }" class="tb-img-uploader">
|
||
<div uk-form-custom>
|
||
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
|
||
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
|
||
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
|
||
</div>
|
||
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
|
||
@click="files = null"></button>
|
||
</div>
|
||
</div>
|
||
{{ if ne .Question.ReceiveReplyEmail "" }}
|
||
<div class="uk-alert-warning uk-text-small" uk-alert>
|
||
<p>{{t .LangCode "qitem.emailNotifyHint"}}</p>
|
||
</div>
|
||
{{ end }}
|
||
</form>
|
||
<!-- 回答(左)+ 阅读状态(右)同一行,压缩空间;tb-ans-row 保证左右按钮等高 -->
|
||
<div class="uk-flex uk-flex-middle uk-flex-wrap uk-margin-small-top tb-ans-row" style="gap: 12px; justify-content: space-between;">
|
||
<div class="uk-flex uk-flex-middle uk-flex-wrap tb-ans-actions" style="gap: 8px;">
|
||
<button type="submit" form="tb-answer-form" class="uk-button uk-button-primary">{{t .LangCode "qitem.submitAnswer"}}</button>
|
||
{{ if .NextUnreadID }}
|
||
<button type="submit" form="tb-answer-form" name="next" value="1" class="uk-button uk-button-default tb-ans-next">{{t .LangCode "qitem.submitAndNext"}}</button>
|
||
{{ end }}
|
||
</div>
|
||
<div class="uk-flex uk-flex-middle uk-flex-wrap tb-ans-state" style="gap: 8px;">
|
||
{{ if .Question.IsRead }}
|
||
<span class="uk-text-small uk-text-muted">✓ {{t .LangCode "qlist.read"}}</span>
|
||
<form class="uk-display-inline" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/unread">
|
||
{{ .CSRFTokenHTML }}
|
||
<button type="submit" class="uk-button uk-button-default">{{t .LangCode "qitem.markUnread"}}</button>
|
||
</form>
|
||
{{ else }}
|
||
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/read">
|
||
{{ .CSRFTokenHTML }}
|
||
<button type="submit" class="uk-button uk-button-default">{{t .LangCode "qitem.markRead"}}</button>
|
||
</form>
|
||
{{ end }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{ end }}
|
||
|
||
<!-- 已回答的阅读状态(箱主):该行仅此一个元素,居中显示 -->
|
||
{{ if and .CanDelete (ne .Question.Answer "") }}
|
||
<div class="uk-card-body uk-padding-remove-top">
|
||
<div class="uk-text-center">
|
||
<span class="uk-label uk-label-success">✓ {{t .LangCode "qlist.read"}}</span>
|
||
<span class="uk-text-small uk-text-muted">{{t .LangCode "qitem.readLocked"}}</span>
|
||
</div>
|
||
</div>
|
||
{{ end }}
|
||
|
||
{{ if .FollowUps }}
|
||
<div class="uk-card-body uk-padding-remove-top">
|
||
{{ range $index, $elem := .FollowUps }}
|
||
<div class="tb-followup-card uk-margin-small-bottom">
|
||
<p class="uk-text-small uk-text-break uk-margin-remove-bottom"><strong>{{t $.LangCode "followup.content"}}</strong>{{ $elem.Content }}</p>
|
||
{{ if ne $elem.Answer "" }}
|
||
{{ if $.CanDelete }}
|
||
<div id="fu-display-{{ $index }}" class="uk-text-small uk-text-muted uk-text-break uk-margin-small-top">@{{ $.PageUser.Name }}:{{ MarkdownFormat $elem.Answer }}</div>
|
||
<div class="uk-margin-small-top uk-text-right">
|
||
<button type="button" class="uk-button uk-button-default uk-button-small" onclick="tbToggleFollowUpEditor({{ $index }})">{{t $.LangCode "qitem.updateAnswer"}}</button>
|
||
</div>
|
||
<div id="fu-editor-{{ $index }}" style="display:none;">
|
||
<hr>
|
||
<form method="post" action="/_/{{ $.PageUser.Domain }}/{{ $.Question.ID }}/follow-up/{{ $elem.ID }}/answer" class="uk-margin-small-top">
|
||
{{ $.CSRFTokenHTML }}
|
||
<textarea name="answer" class="uk-textarea" rows="3" maxlength="{{ $.Limits.AnswerContentLen }}" placeholder="{{t $.LangCode "followup.answerPlaceholder"}}">{{ $elem.Answer }}</textarea>
|
||
<div class="uk-margin-small-top uk-text-right">
|
||
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t $.LangCode "qitem.updateAnswer"}}</button>
|
||
<button type="button" class="uk-button uk-button-default uk-button-small uk-margin-small-left" onclick="tbToggleFollowUpEditor({{ $index }})">{{t $.LangCode "common.cancel"}}</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
{{ else }}
|
||
<div class="uk-text-small uk-text-muted uk-text-break uk-margin-small-top">@{{ $.PageUser.Name }}:{{ MarkdownFormat $elem.Answer }}</div>
|
||
{{ end }}
|
||
{{ else if $.CanDelete }}
|
||
<form method="post" action="/_/{{ $.PageUser.Domain }}/{{ $.Question.ID }}/follow-up/{{ $elem.ID }}/answer" class="uk-margin-small-top">
|
||
{{ $.CSRFTokenHTML }}
|
||
<textarea name="answer" class="uk-textarea" rows="3" maxlength="{{ $.Limits.AnswerContentLen }}" placeholder="{{t $.LangCode "followup.answerPlaceholder"}}"></textarea>
|
||
<div class="uk-margin-small-top uk-text-right">
|
||
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t $.LangCode "followup.answerBtn"}}</button>
|
||
</div>
|
||
</form>
|
||
{{ else }}
|
||
<p class="uk-text-small uk-text-muted uk-margin-small-top">{{t $.LangCode "followup.waiting"}}</p>
|
||
{{ end }}
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
{{ end }}
|
||
|
||
{{ if and .CanFollowUp (not .CanDelete) }}
|
||
<div class="uk-card-body uk-padding-remove-top">
|
||
<div class="tb-followup-card">
|
||
<h5 class="uk-margin-remove-bottom">{{t .LangCode "followup.askMore"}}</h5>
|
||
<p class="uk-text-small uk-text-muted">{{t .LangCode "followup.remaining" .FollowUpRemaining .FollowUpLimit}}</p>
|
||
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/follow-up">
|
||
{{ .CSRFTokenHTML }}
|
||
<input type="hidden" name="token" value="{{ .QuestionToken }}">
|
||
<textarea name="content" class="uk-textarea" rows="3" maxlength="{{ .Limits.FollowUpContentLen }}" placeholder="{{t .LangCode "followup.contentPlaceholder"}}"></textarea>
|
||
<div class="uk-margin uk-text-center">
|
||
<button type="submit" class="uk-button uk-button-primary">{{t .LangCode "followup.submit"}}</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{{ end }}
|
||
|
||
{{ if and .WaitingFollowUpReply (not .CanDelete) }}
|
||
<div class="uk-card-body uk-padding-remove-top">
|
||
<div class="tb-followup-card uk-text-small uk-text-muted uk-text-center">{{t .LangCode "followup.waitingReply"}}</div>
|
||
</div>
|
||
{{ end }}
|
||
|
||
<div class="uk-card-footer">
|
||
{{template "base/alert" .}}
|
||
|
||
{{ if .CanDelete }}
|
||
|
||
{{ if .Question.IsPrivate }}
|
||
<form class="uk-float-right" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/set-public">
|
||
{{ .CSRFTokenHTML }}
|
||
<button type="submit" class="uk-button uk-button-default uk-button-small">{{t .LangCode "qitem.public"}}</button>
|
||
</form>
|
||
{{ else }}
|
||
<form class="uk-float-right" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/set-private">
|
||
{{ .CSRFTokenHTML }}
|
||
<button type="submit" class="uk-button uk-button-default uk-button-small">{{t .LangCode "qitem.private"}}</button>
|
||
</form>
|
||
{{ end }}
|
||
|
||
<a class="uk-button uk-button-danger uk-button-small" href="#">{{t .LangCode "qitem.delete"}}</a>
|
||
<div class="uk-dropbar uk-dropbar-top" uk-drop="stretch: x; mode: click">
|
||
<div class="uk-card uk-card-default uk-card-body">
|
||
<h3 class="uk-card-title">{{t .LangCode "qitem.danger"}}</h3>
|
||
<p>{{t .LangCode "qitem.deleteConfirm"}}</p>
|
||
<form class="uk-float-right"
|
||
method="post"
|
||
action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/delete">
|
||
{{ .CSRFTokenHTML }}
|
||
<button class="uk-button uk-button-danger">{{t .LangCode "qitem.deleteConfirmBtn"}}</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{{ end }}
|
||
|
||
<div class="uk-margin uk-text-center">
|
||
{{ $backTo := printf "/_/%s" .PageUser.Domain }}
|
||
{{ if eq .BackFrom "admin" }}{{ $backTo = .BackTo }}{{ end }}
|
||
<a class="uk-button uk-button-link" href="{{ $backTo }}">{{t .LangCode "qitem.backToBox" .PageUser.Name}}</a>
|
||
{{ if and .CanDelete .NextUnreadID }}
|
||
<!-- 下一条未读:标记本条已读并跳转,会改状态,故用 POST 表单而非链接 -->
|
||
<span class="tb-foot-sep" aria-hidden="true">·</span>
|
||
<form class="uk-display-inline" method="post" action="{{ if .NextUnreadAction }}{{ .NextUnreadAction }}{{ else }}/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/next-unread{{ end }}">
|
||
{{ .CSRFTokenHTML }}
|
||
<button type="submit" class="uk-button uk-button-link" title="{{t .LangCode "qitem.nextUnreadTitle"}}">{{t .LangCode "qitem.nextUnread"}} ›</button>
|
||
</form>
|
||
{{ end }}
|
||
</div>
|
||
|
||
<!-- Box owner can't create new question here. -->
|
||
{{if not .IsOwnPage }}
|
||
<h5 class="uk-text-center">{{t .LangCode "qitem.askMore"}}</h5>
|
||
{{template "question/new-question-template" .}}
|
||
{{ end }}
|
||
|
||
<hr class="uk-divider-icon">
|
||
<p class="uk-text-left uk-text-muted uk-text-small">{{t .LangCode "ask.history" .PageUser.Name}} ({{ .AnsweredCount }})</p>
|
||
{{ template "question/history-template" . }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
function tbToggleAnswerEditor() {
|
||
var disp = document.getElementById('answer-display');
|
||
var trig = document.getElementById('answer-edit-trigger');
|
||
var ed = document.getElementById('answer-editor');
|
||
if (!ed) return;
|
||
if (ed.style.display === 'none') {
|
||
ed.style.display = 'block';
|
||
if (disp) disp.style.display = 'none';
|
||
if (trig) trig.style.display = 'none';
|
||
} else {
|
||
ed.style.display = 'none';
|
||
if (disp) disp.style.display = 'block';
|
||
if (trig) trig.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
function tbToggleFollowUpEditor(idx) {
|
||
var disp = document.getElementById('fu-display-' + idx);
|
||
var ed = document.getElementById('fu-editor-' + idx);
|
||
if (!ed) return;
|
||
if (ed.style.display === 'none') {
|
||
ed.style.display = 'block';
|
||
if (disp) disp.style.display = 'none';
|
||
} else {
|
||
ed.style.display = 'none';
|
||
if (disp) disp.style.display = 'block';
|
||
}
|
||
}
|
||
</script>
|
||
{{template "base/footer" .}}
|