FIDE titles

pull/83/head
Thibault Duplessis 2014-02-27 01:18:09 +01:00
parent 821626cfa2
commit 16af702189
15 changed files with 74 additions and 10 deletions

View File

@ -1,9 +1,9 @@
package controllers
import lila.app._
import views._
import lila.security.Permission
import lila.user.UserRepo
import views._
import play.api.mvc._
import play.api.mvc.Results._
@ -38,9 +38,21 @@ object Mod extends LilaController {
me => modApi.reopenAccount(me.id, username) inject redirect(username)
}
def setTitle(username: String) = AuthBody { implicit ctx =>
me =>
implicit def req = ctx.body
if (isGranted(_.SetTitle))
lila.user.DataForm.title.bindFromRequest.fold(
err => fuccess(Redirect(routes.User.show(username))),
title => modApi.setTitle(me.id, username, title) inject redirect(username, false)
)
else fuccess(authorizationFailed(ctx.req))
}
def log = Auth { implicit ctx =>
me => modLogApi.recent map { html.mod.log(_) }
}
def redirect(username: String) = Redirect(routes.User.show(username).url + "?mod")
def redirect(username: String, mod: Boolean = true) =
Redirect(routes.User.show(username).url + mod.??("?mod"))
}

View File

@ -1,6 +1,6 @@
@(field: play.api.data.Field, options: Iterable[(Any,String)], default: Option[String] = None)
<select id="@field.id" name="@field.name">
<select name="@field.name">
@default.map { d =>
<option value="">@d</option>
}

View File

@ -34,6 +34,11 @@
</form>
}
}
@if(isGranted(_.SetTitle)) {
<form class="fide_title" method="post" action="@routes.Mod.setTitle(u.username)">
@base.select(lila.user.DataForm.title.fill(u.title)("title"), lila.user.User.titles, "No FIDE title".some)
</form>
}
</div>
<div class="user_spy">

View File

@ -90,6 +90,10 @@ themepicker = true) {
<span data-icon="j" class="is4"></span>
@trans.thisPlayerUsesChessComputerAssistance()
</div>
} else {
@u.title.flatMap(lila.user.User.titlesMap.get).map { title =>
<p data-icon="E" class="title"> @title</p>
}
}
@u.profile.map { p =>
@p.nonEmptyRealName.map { name =>

View File

@ -156,6 +156,7 @@ POST /mod/:username/troll controllers.Mod.troll(username: String)
POST /mod/:username/ban controllers.Mod.ban(username: String)
POST /mod/:username/close controllers.Mod.closeAccount(username: String)
POST /mod/:username/reopen controllers.Mod.reopenAccount(username: String)
POST /mod/:username/title controllers.Mod.setTitle(username: String)
POST /mod/:ip/ipban controllers.Mod.ipban(ip: String)
GET /mod/log controllers.Mod.log

View File

@ -15,10 +15,12 @@ final class DetectLanguage(url: String, key: String) {
private implicit val DetectionReads = Json.reads[Detection]
private val messageMaxLength = 2000
def apply(message: String): Fu[Option[Lang]] =
WS.url(url).post(Map(
"key" -> Seq(key),
"q" -> Seq(message)
"q" -> Seq(message take messageMaxLength)
)) map { response =>
(response.json \ "data" \ "detections").as[List[Detection]]
.filter(_.isReliable)

View File

@ -49,6 +49,10 @@ final class ModApi(
}
}
def setTitle(mod: String, username: String, title: Option[String]): Funit = withUser(username) { user =>
UserRepo.setTitle(user.id, title) >> logApi.setTitle(mod, user.id, title)
}
def ipban(mod: String, ip: String): Funit =
(firewall blockIp ip) >> logApi.ipban(mod, ip)

View File

@ -21,6 +21,8 @@ case class Modlog(
case Modlog.reopenAccount => "reopen account"
case Modlog.openTopic => "reopen topic"
case Modlog.closeTopic => "close topic"
case Modlog.setTitle => "set FIDE title"
case Modlog.removeTitle => "remove FIDE title"
case a => a
}
}
@ -39,6 +41,8 @@ object Modlog {
val deletePost = "deletePost"
val closeTopic = "closeTopic"
val openTopic = "openTopic"
val setTitle = "setTitle"
val removeTitle = "removeTitle"
import lila.db.JsTube
import JsTube.Helpers._

View File

@ -26,6 +26,11 @@ final class ModlogApi {
Modlog(mod, user.some, Modlog.reopenAccount)
}
def setTitle(mod: String, user: String, title: Option[String]) = add {
val name = title flatMap lila.user.User.titlesMap.get
Modlog(mod, user.some, name.isDefined.fold(Modlog.setTitle, Modlog.removeTitle), details = name)
}
def ipban(mod: String, ip: String) = add {
Modlog(mod, none, Modlog.ipban, ip.some)
}

View File

@ -19,13 +19,15 @@ object Permission {
case object IpBan extends Permission("ROLE_IP_BAN", List(UserSpy))
case object CloseAccount extends Permission("ROLE_CLOSE_ACCOUNT", List(UserSpy))
case object ReopenAccount extends Permission("ROLE_REOPEN_ACCOUNT", List(UserSpy))
case object SetTitle extends Permission("ROLE_SET_TITLE", List(UserSpy))
case object SeeReport extends Permission("ROLE_SEE_REPORT", Nil)
case object Hunter extends Permission("ROLE_HUNTER", List(
ViewBlurs, MarkEngine, StaffForum, UserSpy, UserEvaluate, SeeReport))
ViewBlurs, MarkEngine, StaffForum, UserSpy, UserEvaluate, SeeReport))
case object Admin extends Permission("ROLE_ADMIN", List(
ViewBlurs, MarkTroll, MarkEngine, StaffForum, ModerateForum, UserSpy, UserEvaluate, SeeReport, IpBan, CloseAccount, ReopenAccount))
ViewBlurs, MarkTroll, MarkEngine, StaffForum, ModerateForum, UserSpy,
UserEvaluate, SeeReport, IpBan, CloseAccount, ReopenAccount, SetTitle))
case object SuperAdmin extends Permission("ROLE_SUPER_ADMIN", List(Admin))

View File

@ -32,4 +32,6 @@ object DataForm {
"the new passwords don't match",
_.samePasswords
))
val title = Form(single("title" -> optional(nonEmptyText)))
}

View File

@ -20,6 +20,7 @@ case class User(
profile: Option[Profile] = None,
engine: Boolean = false,
toints: Int = 0,
title: Option[String] = None,
createdAt: DateTime,
seenAt: Option[DateTime],
lang: Option[String]) extends Ordered[User] {
@ -67,6 +68,14 @@ object User {
def normalize(username: String) = username.toLowerCase
val titles = Seq(
"CM" -> "Candidate Master (CM)",
"FM" -> "FIDE Master (FM)",
"IM" -> "International Master (IM)",
"GM" -> "Grand Master (CM)")
val titlesMap = titles.toMap
object BSONFields {
val id = "_id"
val username = "username"
@ -85,6 +94,7 @@ object User {
val createdAt = "createdAt"
val seenAt = "seenAt"
val lang = "lang"
val title = "title"
def glicko(perf: String) = s"$perfs.$perf.gl"
}
@ -116,7 +126,8 @@ object User {
toints = r nIntD toints,
createdAt = r date createdAt,
seenAt = r dateO seenAt,
lang = r strO lang)
lang = r strO lang,
title = r strO title)
def writes(w: BSON.Writer, o: User) = BSONDocument(
id -> o.id,
@ -135,7 +146,8 @@ object User {
toints -> w.intO(o.toints),
createdAt -> o.createdAt,
seenAt -> o.seenAt,
lang -> o.lang)
lang -> o.lang,
title -> o.title)
}
private[user] lazy val tube = lila.db.BsTube(userBSONHandler)

View File

@ -93,6 +93,11 @@ trait UserRepo {
def setProfile(id: ID, profile: Profile): Funit =
$update($select(id), $setBson("profile" -> Profile.tube.handler.write(profile)))
def setTitle(id: ID, title: Option[String]): Funit = title match {
case Some(t) => $update.field(id, "title", t)
case None => $update($select(id), $unset("title"))
}
def setEvaluated(id: ID, v: Boolean): Funit = $update.field(id, "evaluated", v)
def isEvaluated(id: ID): Fu[Boolean] =
$primitive.one($select(id), "evaluated")(_.asOpt[Boolean]) map (~_)

View File

@ -20,6 +20,9 @@ $(function() {
var $zone = $("div.user_show .mod_zone");
if ($zone.is(':visible')) $zone.hide();
else $zone.html("Loading...").show().load($(this).attr("href"), function() {
$(this).find('form.fide_title select').on('change', function() {
$(this).parent('form').submit();
});
$('body').trigger('lichess.content_loaded');
});
return false;

View File

@ -21,7 +21,11 @@ div.user_show div.content_box_top > img.trophy {
margin-top: -30px;
margin-right: 10px;
}
div.user_show .title {
margin-top: 1em;
font-weight: bold;
font-size: 1.4em;
}
div.user_show .social {
position: relative;
border-top: 0;
@ -138,7 +142,6 @@ div.user_show .mod_zone .same {
font-weight: bold;
}
div.user_show .mod_zone form {
margin: 0 25px 0 0;
display: inline;
}
div.user_show .mod_zone .listings > div {