appeal WIP
parent
51efdd2d34
commit
c52a7d5f78
|
@ -71,6 +71,7 @@ final class LilaComponents(ctx: ApplicationLoader.Context)
|
|||
lazy val account: Account = wire[Account]
|
||||
lazy val analyse: Analyse = wire[Analyse]
|
||||
lazy val api: Api = wire[Api]
|
||||
lazy val appeal: Appeal = wire[Appeal]
|
||||
lazy val auth: Auth = wire[Auth]
|
||||
lazy val blog: Blog = wire[Blog]
|
||||
lazy val bookmark: Bookmark = wire[Bookmark]
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
package controllers
|
||||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import play.api.mvc._
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app._
|
||||
import views._
|
||||
|
||||
final class Appeal(env: Env) extends LilaController(env) {
|
||||
|
||||
// def form =
|
||||
// Auth { implicit ctx => _ =>
|
||||
// env.appeal.forms.create map {
|
||||
// case (form, captcha) => Ok(html.report.form(form, user, captcha))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
def home =
|
||||
Auth { implicit ctx => me =>
|
||||
env.appeal.api.mine(me) map { appeal =>
|
||||
Ok(html.appeal2.home(appeal, env.appeal.forms.text))
|
||||
}
|
||||
}
|
||||
|
||||
def post =
|
||||
AuthBody { implicit ctx => me =>
|
||||
implicit val req = ctx.body
|
||||
env.appeal.forms.text
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
err =>
|
||||
env.appeal.api.mine(me) map { appeal =>
|
||||
BadRequest(html.appeal2.home(appeal, err))
|
||||
},
|
||||
text =>
|
||||
env.appeal.api.post(text, me) inject Redirect(routes.Appeal.home()).flashSuccess
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@ trait StringHelper { self: NumberHelper =>
|
|||
|
||||
def urlencode(str: String): String = java.net.URLEncoder.encode(str, "US-ASCII")
|
||||
|
||||
def when(cond: Boolean, str: String) = cond ?? str
|
||||
|
||||
private val NumberFirstRegex = """(\d++)\s(.+)""".r
|
||||
private val NumberLastRegex = """\s(\d++)$""".r.unanchored
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package views.html
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.String.html.richText
|
||||
import play.api.data.Form
|
||||
import lila.appeal.Appeal
|
||||
import controllers.routes
|
||||
import lila.user.User
|
||||
import play.api.i18n.Lang
|
||||
|
||||
object appeal2 {
|
||||
|
||||
def home(appeal: Option[Appeal], textForm: Form[_])(implicit ctx: Context) =
|
||||
views.html.base.layout(
|
||||
moreCss = frag(
|
||||
cssTag("form3"),
|
||||
cssTag("appeal")
|
||||
),
|
||||
title = "Appeal"
|
||||
) {
|
||||
main(cls := "page-small box box-pad page appeal")(
|
||||
appeal match {
|
||||
case Some(a) => myAppeal(a, textForm)
|
||||
case None => newAppeal(textForm)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def newAppeal(textForm: Form[_])(implicit ctx: Context) =
|
||||
frag(
|
||||
h1("Appeal a moderation decision"),
|
||||
renderHelp,
|
||||
div(cls := "body")(
|
||||
renderForm(textForm)
|
||||
)
|
||||
)
|
||||
|
||||
def myAppeal(appeal: Appeal, textForm: Form[_])(implicit ctx: Context) =
|
||||
frag(
|
||||
h1(if (appeal.isOpen) "Ongoing appeal" else "Closed appeal"),
|
||||
standardFlash(),
|
||||
renderHelp,
|
||||
div(cls := "body")(
|
||||
renderStatus(appeal),
|
||||
appeal.msgs.map { msg =>
|
||||
div(cls := "appeal__msg")(
|
||||
div(cls := "appeal__msg__header")(
|
||||
renderUser(appeal, msg.by),
|
||||
momentFromNowOnce(msg.at)
|
||||
),
|
||||
div(cls := "appeal__msg__text")(richText(msg.text))
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
private def renderStatus(appeal: Appeal) =
|
||||
div(cls := "appeal__status")(
|
||||
appeal.status match {
|
||||
case Appeal.Status.Closed => "The appeal is closed."
|
||||
case Appeal.Status.Unread => "The appeal is being reviewed by the moderation team."
|
||||
case Appeal.Status.Read => "The appeal is open and awaiting your input."
|
||||
case Appeal.Status.Muted => "The appeal is on hold."
|
||||
}
|
||||
)
|
||||
|
||||
private def renderHelp =
|
||||
div(cls := "appeal__help")(
|
||||
p(
|
||||
"If your account has been restricted for violation of ",
|
||||
a(href := routes.Page.tos())("the Lichess rules"),
|
||||
", and you are absolutely certain that you did not break ",
|
||||
a(href := routes.Page.tos())("said rules"),
|
||||
", then you may file an appeal here."
|
||||
),
|
||||
p(
|
||||
"If you did break ",
|
||||
a(href := routes.Page.tos())("the Lichess rules"),
|
||||
", even once, then your account is lost. We don't have the luxury of being forgiving."
|
||||
),
|
||||
p(
|
||||
strong("Do not lie in an appeal"),
|
||||
". It would result in a lifetime ban, ",
|
||||
"and the automatic termination of any future account you make."
|
||||
)
|
||||
)
|
||||
|
||||
private def renderUser(appeal: Appeal, userId: User.ID)(implicit lang: Lang) =
|
||||
userIdLink((if (appeal isAbout userId) userId else User.lichessId).some)
|
||||
|
||||
private def renderForm(form: Form[_])(implicit ctx: Context) =
|
||||
postForm(action := routes.Appeal.post())(
|
||||
form3.globalError(form),
|
||||
form3.group(form("text"), trans.description())(
|
||||
form3.textarea(_)(rows := 8)
|
||||
),
|
||||
form3.action(form3.submit(trans.send()))
|
||||
)
|
||||
}
|
|
@ -50,7 +50,7 @@ lazy val modules = Seq(
|
|||
analyse, mod, round, pool, lobby, setup,
|
||||
importer, tournament, simul, relation, report, pref,
|
||||
evaluation, chat, puzzle, tv, coordinate, blog,
|
||||
history, video, shutup, push,
|
||||
history, video, shutup, push, appeal,
|
||||
playban, insight, perfStat, slack, quote, challenge,
|
||||
study, studySearch, fishnet, explorer, learn, plan,
|
||||
event, coach, practice, evalCache, irwin,
|
||||
|
@ -392,6 +392,11 @@ lazy val report = module("report",
|
|||
reactivemongo.bundle
|
||||
)
|
||||
|
||||
lazy val appeal = module("appeal",
|
||||
Seq(common, db, user),
|
||||
reactivemongo.bundle
|
||||
)
|
||||
|
||||
lazy val explorer = module("explorer",
|
||||
Seq(common, db, game, importer),
|
||||
reactivemongo.bundle
|
||||
|
|
|
@ -538,7 +538,8 @@ POST /report/:id/xfiles controllers.Report.xfiles(id: String)
|
|||
GET /report/:username/cheat-inquiry controllers.Report.currentCheatInquiry(username: String)
|
||||
|
||||
# Appeal
|
||||
GET /appeal controllers.Appeal.form
|
||||
GET /appeal controllers.Appeal.home
|
||||
POST /appeal controllers.Appeal.post
|
||||
|
||||
# Stats
|
||||
GET /stat/rating/distribution/:perf controllers.Stat.ratingDistribution(perf: String)
|
||||
|
|
|
@ -4,12 +4,27 @@ import lila.user.User
|
|||
import org.joda.time.DateTime
|
||||
|
||||
case class Appeal(
|
||||
_id: User.ID,
|
||||
msgs: List[AppealMsg],
|
||||
status: Appeal.Status,
|
||||
createdAt: DateTime,
|
||||
updatedAt: DateTime
|
||||
)
|
||||
_id: User.ID,
|
||||
msgs: Vector[AppealMsg],
|
||||
status: Appeal.Status,
|
||||
createdAt: DateTime,
|
||||
updatedAt: DateTime
|
||||
) {
|
||||
def id = _id
|
||||
def isOpen = status != Appeal.Status.Closed
|
||||
def isAbout(userId: User.ID) = _id == userId
|
||||
|
||||
def post(text: String, by: User) =
|
||||
copy(
|
||||
msgs = msgs :+ AppealMsg(
|
||||
by = by.id,
|
||||
text = text,
|
||||
at = DateTime.now
|
||||
),
|
||||
updatedAt = DateTime.now,
|
||||
status = if (by.id == _id && status == Appeal.Status.Read) Appeal.Status.Unread else status
|
||||
)
|
||||
}
|
||||
|
||||
object Appeal {
|
||||
|
||||
|
@ -18,17 +33,17 @@ object Appeal {
|
|||
}
|
||||
object Status {
|
||||
case object Unread extends Status
|
||||
case object Read extends Status
|
||||
case object Read extends Status
|
||||
case object Closed extends Status
|
||||
case object Muted extends Status
|
||||
val all = List[Status](Unread, Read, Closed, Muted)
|
||||
case object Muted extends Status
|
||||
val all = List[Status](Unread, Read, Closed, Muted)
|
||||
def apply(key: String) = all.find(_.key == key)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
case class AppealMsg(
|
||||
by: User.ID,
|
||||
text: String,
|
||||
at: DateTime
|
||||
by: User.ID,
|
||||
text: String,
|
||||
at: DateTime
|
||||
)
|
||||
|
|
|
@ -1,9 +1,37 @@
|
|||
package lila.appeal
|
||||
|
||||
import lila.db.dsl._
|
||||
import lila.user.User
|
||||
import org.joda.time.DateTime
|
||||
|
||||
final class AppealApi(
|
||||
coll: Coll
|
||||
coll: Coll
|
||||
)(implicit ec: scala.concurrent.ExecutionContext) {
|
||||
|
||||
import BsonHandlers._
|
||||
|
||||
def mine(me: User): Fu[Option[Appeal]] = coll.byId[Appeal](me.id)
|
||||
|
||||
def post(text: String, me: User) =
|
||||
mine(me) flatMap {
|
||||
case None =>
|
||||
val appeal =
|
||||
Appeal(
|
||||
_id = me.id,
|
||||
msgs = Vector(
|
||||
AppealMsg(
|
||||
by = me.id,
|
||||
text = text,
|
||||
at = DateTime.now
|
||||
)
|
||||
),
|
||||
status = Appeal.Status.Unread,
|
||||
createdAt = DateTime.now,
|
||||
updatedAt = DateTime.now
|
||||
)
|
||||
coll.insert.one(appeal) inject appeal
|
||||
case Some(prev) =>
|
||||
val appeal = prev.post(text, me)
|
||||
coll.update.one($id(appeal.id), appeal) inject appeal
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,66 +2,10 @@ package lila.appeal
|
|||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import play.api.data.validation._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.user.User
|
||||
final class AppealForm {
|
||||
|
||||
final class DataForm {
|
||||
|
||||
// val create = Form(
|
||||
// mapping(
|
||||
// "username" -> lila.user.DataForm.historicalUsernameField.verifying(
|
||||
// "Unknown username", {
|
||||
// blockingFetchUser(_).isDefined
|
||||
// }
|
||||
// ),
|
||||
// "reason" -> text.verifying("error.required", Reason.keys contains _),
|
||||
// "text" -> text(minLength = 5, maxLength = 2000),
|
||||
// "gameId" -> text,
|
||||
// "move" -> text
|
||||
// )({
|
||||
// case (username, reason, text, gameId, move) =>
|
||||
// ReportSetup(
|
||||
// user = blockingFetchUser(username) err "Unknown username " + username,
|
||||
// reason = reason,
|
||||
// text = text,
|
||||
// gameId = gameId,
|
||||
// move = move
|
||||
// )
|
||||
// })(_.export.some).verifying(captchaFailMessage, validateCaptcha _).verifying(cheatLinkConstraint)
|
||||
// )
|
||||
|
||||
// def createWithCaptcha = withCaptcha(create)
|
||||
|
||||
// val flag = Form(
|
||||
// mapping(
|
||||
// "username" -> lila.user.DataForm.historicalUsernameField,
|
||||
// "resource" -> nonEmptyText,
|
||||
// "text" -> text(minLength = 3, maxLength = 140)
|
||||
// )(ReportFlag.apply)(ReportFlag.unapply)
|
||||
// )
|
||||
|
||||
// private def blockingFetchUser(username: String) =
|
||||
// lightUserAsync(User normalize username).await(1 second, "reportUser")
|
||||
// }
|
||||
|
||||
// private[report] case class ReportFlag(
|
||||
// username: String,
|
||||
// resource: String,
|
||||
// text: String
|
||||
// )
|
||||
|
||||
// private[report] case class ReportSetup(
|
||||
// user: LightUser,
|
||||
// reason: String,
|
||||
// text: String,
|
||||
// gameId: String,
|
||||
// move: String
|
||||
// ) {
|
||||
|
||||
// def suspect = SuspectId(user.id)
|
||||
|
||||
// def export = (user.name, reason, text, gameId, move)
|
||||
val text = Form(
|
||||
single("text" -> nonEmptyText)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,6 @@ private[appeal] object BsonHandlers {
|
|||
s => BSONString(s.key)
|
||||
)
|
||||
|
||||
// implicit val appealMsgHandler = Macros.handler[AppealMsg]
|
||||
implicit val appealMsgHandler = Macros.handler[AppealMsg]
|
||||
implicit val appealHandler = Macros.handler[Appeal]
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ final class Env(
|
|||
|
||||
private val coll = db(CollName("appeal"))
|
||||
|
||||
// lazy val forms = wire[ClasForm]
|
||||
lazy val forms = wire[AppealForm]
|
||||
|
||||
lazy val api: AppealApi = wire[AppealApi]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
.appeal {
|
||||
&__help {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
&__status {
|
||||
@extend %box-radius;
|
||||
|
||||
padding: 1em 1.5em;
|
||||
margin-bottom: 2em;
|
||||
background: $c-bg-zebra;
|
||||
|
||||
&::before {
|
||||
@extend %data-icon;
|
||||
|
||||
content: '';
|
||||
font-size: 2em;
|
||||
margin-right: .4em;
|
||||
}
|
||||
}
|
||||
|
||||
&__msg {
|
||||
padding: 1em 1.5em;
|
||||
margin-bottom: 2em;
|
||||
border-left: 10px solid $c-bg-zebra;
|
||||
|
||||
&__header {
|
||||
@extend %flex-between;
|
||||
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
&__ text {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
@import "../../../common/css/plugin";
|
||||
@import "../appeal";
|
|
@ -0,0 +1,2 @@
|
|||
@import '../../../common/css/theme/dark';
|
||||
@import 'appeal';
|
|
@ -0,0 +1,2 @@
|
|||
@import '../../../common/css/theme/light';
|
||||
@import 'appeal';
|
|
@ -0,0 +1,2 @@
|
|||
@import '../../../common/css/theme/transp';
|
||||
@import 'appeal';
|
Loading…
Reference in New Issue