rewrite forum forms
parent
68377f6a56
commit
1dfc4a9054
|
@ -21,37 +21,37 @@ object ForumPost extends LilaController with ForumController {
|
|||
}
|
||||
|
||||
def create(categSlug: String, slug: String, page: Int) = OpenBody { implicit ctx =>
|
||||
CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
CategGrantWrite(categSlug) {
|
||||
implicit val req = ctx.body
|
||||
OptionFuResult(topicApi.show(categSlug, slug, page, ctx.troll)) {
|
||||
case (categ, topic, posts) =>
|
||||
if (topic.closed) fuccess(BadRequest("This topic is closed"))
|
||||
else if (topic.isOld) fuccess(BadRequest("This topic is archived"))
|
||||
else forms.post.bindFromRequest.fold(
|
||||
err => for {
|
||||
captcha <- forms.anyCaptcha
|
||||
unsub <- ctx.userId ?? Env.timeline.status(s"forum:${topic.id}")
|
||||
canModCateg <- isGrantedMod(categ.slug)
|
||||
} yield BadRequest(html.forum.topic.show(categ, topic, posts, Some(err -> captcha), unsub, canModCateg = canModCateg)),
|
||||
data => postApi.makePost(categ, topic, data) map { post =>
|
||||
CategGrantWrite(categSlug) {
|
||||
implicit val req = ctx.body
|
||||
OptionFuResult(topicApi.show(categSlug, slug, page, ctx.troll)) {
|
||||
case (categ, topic, posts) =>
|
||||
if (topic.closed) fuccess(BadRequest("This topic is closed"))
|
||||
else if (topic.isOld) fuccess(BadRequest("This topic is archived"))
|
||||
else forms.post.bindFromRequest.fold(
|
||||
err => for {
|
||||
captcha <- forms.anyCaptcha
|
||||
unsub <- ctx.userId ?? Env.timeline.status(s"forum:${topic.id}")
|
||||
canModCateg <- isGrantedMod(categ.slug)
|
||||
} yield BadRequest(html.forum.topic.show(categ, topic, posts, Some(err -> captcha), unsub, canModCateg = canModCateg)),
|
||||
data => CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
postApi.makePost(categ, topic, data) map { post =>
|
||||
Redirect(routes.ForumPost.redirect(post.id))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def edit(postId: String) = AuthBody { implicit ctx => me =>
|
||||
implicit val req = ctx.body
|
||||
|
||||
forms.postEdit.bindFromRequest.fold(
|
||||
err => Redirect(routes.ForumPost.redirect(postId)).fuccess,
|
||||
data =>
|
||||
data => CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
postApi.editPost(postId, data.changes, me).map { post =>
|
||||
Redirect(routes.ForumPost.redirect(post.id))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,28 +16,26 @@ object ForumTopic extends LilaController with ForumController {
|
|||
|
||||
def form(categSlug: String) = Open { implicit ctx =>
|
||||
NotForKids {
|
||||
CategGrantWrite(categSlug) {
|
||||
OptionFuOk(CategRepo bySlug categSlug) { categ =>
|
||||
forms.anyCaptcha map { html.forum.topic.form(categ, forms.topic, _) }
|
||||
}
|
||||
OptionFuOk(CategRepo bySlug categSlug) { categ =>
|
||||
forms.anyCaptcha map { html.forum.topic.form(categ, forms.topic, _) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def create(categSlug: String) = OpenBody { implicit ctx =>
|
||||
CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
CategGrantWrite(categSlug) {
|
||||
implicit val req = ctx.body
|
||||
OptionFuResult(CategRepo bySlug categSlug) { categ =>
|
||||
forms.topic.bindFromRequest.fold(
|
||||
err => forms.anyCaptcha map { captcha =>
|
||||
BadRequest(html.forum.topic.form(categ, err, captcha))
|
||||
},
|
||||
data => topicApi.makeTopic(categ, data) map { topic =>
|
||||
CategGrantWrite(categSlug) {
|
||||
implicit val req = ctx.body
|
||||
OptionFuResult(CategRepo bySlug categSlug) { categ =>
|
||||
forms.topic.bindFromRequest.fold(
|
||||
err => forms.anyCaptcha map { captcha =>
|
||||
BadRequest(html.forum.topic.form(categ, err, captcha))
|
||||
},
|
||||
data => CreateRateLimit(HTTPRequest lastRemoteAddress ctx.req) {
|
||||
topicApi.makeTopic(categ, data.pp) map { topic =>
|
||||
Redirect(routes.ForumTopic.show(categ.slug, topic.slug, 1))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ trait AssetHelper { self: I18nHelper with SecurityHelper =>
|
|||
cssTag(name).body
|
||||
} mkString ""
|
||||
}
|
||||
def cssTags(names: Map[String, Boolean]): Html =
|
||||
cssTags(names.collect { case (k, true) => k }.toList: _*)
|
||||
|
||||
def cssVendorTag(name: String) = cssAt("vendor/" + name)
|
||||
|
||||
|
|
|
@ -69,18 +69,29 @@ trait FormHelper { self: I18nHelper =>
|
|||
s"""<select id="${id(field)}" name="${field.name}" class="form-control">$defaultH$optionsH</select>"""
|
||||
}
|
||||
|
||||
def textarea(field: Field, required: Boolean = false, rows: Option[Int] = None) = Html {
|
||||
def textarea(
|
||||
field: Field,
|
||||
klass: String = "",
|
||||
required: Boolean = false,
|
||||
rows: Option[Int] = None,
|
||||
attrs: String = ""
|
||||
) = Html {
|
||||
val rowsH = rows ?? { r => s""" rows=$r""" }
|
||||
s"""<textarea id="${id(field)}" name="${field.name}" class="form-control"$rowsH${required ?? " required"}>${~field.value}</textarea>"""
|
||||
s"""<textarea id="${id(field)}" name="${field.name}" class="form-control $klass"$rowsH${required ?? " required"}$attrs>${~field.value}</textarea>"""
|
||||
}
|
||||
|
||||
def actions(html: Html) = Html {
|
||||
s"""<div class="form-actions">$html</div>"""
|
||||
}
|
||||
|
||||
def submit(name: Html, icon: Option[String] = Some("E")) = Html {
|
||||
def submit(
|
||||
content: Html,
|
||||
icon: Option[String] = Some("E"),
|
||||
nameValue: Option[(String, String)] = None
|
||||
) = Html {
|
||||
val iconH = icon ?? { i => s""" data-icon="$i"""" }
|
||||
s"""<button class="submit button${icon.isDefined ?? " text"} type="submit"$iconH>$name</button>"""
|
||||
val nameH = nameValue ?? { case (n, v) => s""" name="$n" value="$v"""" }
|
||||
s"""<button class="submit button${icon.isDefined ?? " text"} type="submit"$iconH$nameH>$content</button>"""
|
||||
}
|
||||
|
||||
def hidden(field: Field, value: Option[String] = None) = Html {
|
||||
|
@ -96,10 +107,11 @@ trait FormHelper { self: I18nHelper =>
|
|||
minLength: Int = 0,
|
||||
maxLength: Int = 0,
|
||||
autocomplete: Boolean = true,
|
||||
autofocus: Boolean = false,
|
||||
pattern: Option[String] = None,
|
||||
attrs: String = ""
|
||||
) = Html {
|
||||
val options = s"""${placeholder ?? { p => s""" placeholder="$p"""" }}${required ?? " required"}${(minLength > 0) ?? s"minlength=$minLength"}${(maxLength > 0) ?? s"maxlength=$maxLength"}${!autocomplete ?? """autocomplete="off""""}${pattern ?? { p => s""" pattern="$p"""" }} $attrs"""
|
||||
val options = s"""${placeholder ?? { p => s""" placeholder="$p"""" }}${required ?? " required"}${(minLength > 0) ?? s"minlength=$minLength"}${(maxLength > 0) ?? s"maxlength=$maxLength"}${!autocomplete ?? """autocomplete="off""""}${autofocus ?? " autofocus"}${pattern ?? { p => s""" pattern="$p"""" }} $attrs"""
|
||||
s"""<input type="typ" id="${id(field)}" name="${field.name}" value="${~field.value}"$options class="form-control $klass"/>"""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
@(title: String, searchText: String = "", side: Option[Html] = None, menu: Option[Html] = None, moreJs: Html = emptyHtml, openGraph: Option[lila.app.ui.OpenGraph] = None)(body: Html)(implicit ctx: Context)
|
||||
@(title: String, searchText: String = "", side: Option[Html] = None, menu: Option[Html] = None, moreJs: Html = emptyHtml, formCss: Boolean = false, openGraph: Option[lila.app.ui.OpenGraph] = None)(body: Html)(implicit ctx: Context)
|
||||
|
||||
@base.layout(
|
||||
title = title,
|
||||
side = side,
|
||||
menu = menu,
|
||||
moreCss = cssTag("forum.css"),
|
||||
moreCss = cssTags(Map("forum.css" -> true, "form3.css" -> formCss)),
|
||||
moreJs = moreJs,
|
||||
openGraph = openGraph) {
|
||||
<div id="lichess_forum" class="content_box no_padding">
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
@(text: Field, topic: Option[lila.forum.Topic])(implicit ctx: Context)
|
||||
<label>
|
||||
<span class="required">@trans.message()</span>
|
||||
<div>
|
||||
@topic match {
|
||||
case Some(topic) => { <textarea class="post-text-area" name="@text.name" data-topic="@topic.id">@text.value</textarea> }
|
||||
case None => { <textarea class="post-text-area" name="@text.name">@text.value</textarea> }
|
||||
}
|
||||
@errMsg(text)
|
||||
</div>
|
||||
</label>
|
||||
@form3.group(text, trans.message()) { f =>
|
||||
@form3.textarea(f, klass = "post-text-area", rows = 10.some, required = true, attrs = topic.??(t => s"""data-topic="${t.id}""""))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@(categ: lila.forum.Categ, form: Form[_], captcha: lila.common.Captcha)(implicit ctx: Context)
|
||||
|
||||
@forum.layout(title = "New forum topic", moreJs=jsTag("forum-post.js")) {
|
||||
@forum.layout(title = "New forum topic", moreJs = jsTag("forum-post.js"), formCss = true) {
|
||||
<ol class="crumbs">
|
||||
<li><a style="text-decoration:none" data-icon="d" class="is4" href="@routes.ForumCateg.index"> Forum</a></li>
|
||||
<li><h1>@categ.name</h1></li>
|
||||
|
@ -17,21 +17,18 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<form class="wide" action="@routes.ForumTopic.create(categ.slug)" method="POST" novalidate>
|
||||
<label>
|
||||
<span class="required">@trans.subject()</span>
|
||||
<div>
|
||||
<input class="subject" autofocus="true" minlength=3 maxlength=100 value="@form("name").value" type="text" name="@form("name").name" id="@form("name").id">
|
||||
@errMsg(form("name"))
|
||||
</div>
|
||||
</label>
|
||||
<form class="form3 create-topic" action="@routes.ForumTopic.create(categ.slug)" method="POST" novalidate>
|
||||
@form3.group(form("name"), trans.subject()) { f =>
|
||||
@form3.input(f, autofocus = true, required = true, minLength = 3, maxLength = 100)
|
||||
}
|
||||
@forum.post.formFields(form("post")("text"), None)
|
||||
@base.captcha(form("post"), captcha)
|
||||
@errMsg(form("post"))
|
||||
<button class="submit button" type="submit" data-icon="E"> @trans.createTheTopic()</button>
|
||||
@form3.actions {
|
||||
<a href="@routes.ForumCateg.show(categ.slug)">@trans.cancel()</a>
|
||||
@if(isGranted(_.PublicMod)){
|
||||
<button class="button" type="submit" data-icon="" name="post.modIcon" value="true"> Create topic as mod</button>
|
||||
@form3.submit(Html("Create as mod"), nameValue = ("post.modIcon", "true").some, icon = "".some)
|
||||
}
|
||||
@form3.submit(trans.createTheTopic())
|
||||
}
|
||||
<a href="@routes.ForumCateg.show(categ.slug)" style="margin-left:20px">@trans.cancel()</a>
|
||||
</form>
|
||||
}
|
||||
|
|
|
@ -5,20 +5,17 @@
|
|||
@jsTag("embed-analyse.js")
|
||||
}
|
||||
|
||||
@modMenu = {
|
||||
@mod.menu("forum")
|
||||
}
|
||||
|
||||
@title = @{ s"${topic.name} • page ${posts.currentPage}/${posts.nbPages} • ${categ.name}" }
|
||||
|
||||
@forum.layout(
|
||||
title = title,
|
||||
menu = modMenu.some.ifTrue(categ.isStaff),
|
||||
menu = categ.isStaff.option(mod.menu("forum")),
|
||||
moreJs = moreJs,
|
||||
openGraph = lila.app.ui.OpenGraph(
|
||||
title = topic.name,
|
||||
url = s"$netBaseUrl${routes.ForumTopic.show(categ.slug, topic.slug, posts.currentPage).url}",
|
||||
description = shorten(posts.currentPageResults.headOption.??(_.text), 152)).some) {
|
||||
description = shorten(posts.currentPageResults.headOption.??(_.text), 152)).some,
|
||||
formCss = formWithCaptcha.isDefined) {
|
||||
<div class="topic">
|
||||
@categ.team.map { team =>
|
||||
<ol class="crumbs">
|
||||
|
@ -109,15 +106,16 @@ description = shorten(posts.currentPageResults.headOption.??(_.text), 152)).some
|
|||
@formWithCaptcha.map {
|
||||
case (form, captcha) => {
|
||||
<h2 class="postNewTitle" id="reply">@trans.replyToThisTopic()</h2>
|
||||
<form class="wide" action="@routes.ForumPost.create(categ.slug, topic.slug, posts.currentPage)#reply" method="POST" novalidate>
|
||||
<form class="form3 reply" action="@routes.ForumPost.create(categ.slug, topic.slug, posts.currentPage)#reply" method="POST" novalidate>
|
||||
@forum.post.formFields(form("text"), topic.some)
|
||||
@base.captcha(form, captcha)
|
||||
@globalError(form)
|
||||
<button type="submit" class="submit button" data-icon="E"> @trans.reply()</button>
|
||||
@form3.actions {
|
||||
<a href="@routes.ForumCateg.show(categ.slug)">@trans.cancel()</a>
|
||||
@if(isGranted(_.PublicMod)){
|
||||
<button type="submit" class="button" data-icon="" name="modIcon" value="true" /> Reply as mod</button>
|
||||
@form3.submit(Html("Reply as mod"), nameValue = ("post.modIcon", "true").some, icon = "".some)
|
||||
}
|
||||
@form3.submit(trans.reply())
|
||||
}
|
||||
<a href="@routes.ForumCateg.show(categ.slug)" style="margin-left:20px">@trans.cancel()</a>
|
||||
</form>
|
||||
}
|
||||
}.getOrElse {
|
||||
|
|
|
@ -28,7 +28,5 @@ object Captcha {
|
|||
val failMessage = "captcha.fail"
|
||||
|
||||
def isFailed(form: Form.FormLike) =
|
||||
form.errors.exists { e =>
|
||||
e.key == "" && e.messages.has(failMessage)
|
||||
}
|
||||
form.errors.exists { _.messages has failMessage }
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ $(function() {
|
|||
|
||||
$('.post-text-area').one('focus', function() {
|
||||
|
||||
var textarea = this;
|
||||
var textarea = this, topicId = $(this).attr('data-topic');
|
||||
|
||||
lichess.loadScript('vendor/textcomplete.min.js').then(function() {
|
||||
if (topicId) lichess.loadScript('vendor/textcomplete.min.js').then(function() {
|
||||
|
||||
var searchCandidates = function(term, candidateUsers) {
|
||||
return candidateUsers.filter(function(user) {
|
||||
|
@ -37,7 +37,7 @@ $(function() {
|
|||
// forums will be only to read the thread. So the 'thread participants' starts out empty until the post text area
|
||||
// is focused.
|
||||
var threadParticipants = $.ajax({
|
||||
url: "/forum/participants/" + $('.post-text-area').attr('data-topic')
|
||||
url: "/forum/participants/" + topicId
|
||||
});
|
||||
|
||||
var textcomplete = new Textcomplete(new Textcomplete.editors.Textarea(textarea));
|
||||
|
|
|
@ -231,45 +231,14 @@ div.category .description {
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
#lichess_forum form.wide {
|
||||
margin-bottom: 1em;
|
||||
#lichess_forum form.create-topic {
|
||||
margin: 20px;
|
||||
}
|
||||
#lichess_forum form.wide {
|
||||
#lichess_forum form.reply {
|
||||
margin-top: 2em;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
#lichess_forum form.wide label {
|
||||
display: flex;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#lichess_forum form.wide label span {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
margin: 8px 10px 0 0;
|
||||
}
|
||||
#lichess_forum form.wide label span.required:after {
|
||||
content: "*";
|
||||
color: #FF6666;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#lichess_forum form.wide input.subject {
|
||||
width: 500px;
|
||||
}
|
||||
#lichess_forum form.wide textarea {
|
||||
width: 500px;
|
||||
height: 150px;
|
||||
}
|
||||
#lichess_forum form.wide input.author {
|
||||
width: 300px;
|
||||
}
|
||||
#lichess_forum form.wide .submit {
|
||||
margin-left: 110px;
|
||||
}
|
||||
|
||||
#lichess_forum form.wide p.error {
|
||||
color: red;
|
||||
#lichess_forum form.reply label[for=form3-text] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.pagination a, div.pagination span {
|
||||
|
|
Loading…
Reference in New Issue