safer mod notes
parent
ee2d375985
commit
2563a13e98
|
@ -404,7 +404,10 @@ final class User(
|
|||
_ ?? { user =>
|
||||
env.user.forms.note.bindFromRequest.fold(
|
||||
e => err(e)(user),
|
||||
data => env.user.noteApi.write(user, data.text, me, data.mod && isGranted(_.ModNote, me)) inject suc
|
||||
data => {
|
||||
val isMod = data.mod && isGranted(_.ModNote, me)
|
||||
env.user.noteApi.write(user, data.text, me, isMod, isMod && ~data.dox)
|
||||
} inject suc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,22 +99,26 @@ trait FormHelper { self: I18nHelper =>
|
|||
)(
|
||||
div(
|
||||
span(cls := "form-check-input")(
|
||||
st.input(
|
||||
st.id := id(field),
|
||||
name := field.name,
|
||||
value := "true",
|
||||
tpe := "checkbox",
|
||||
cls := "form-control cmn-toggle",
|
||||
field.value.has("true") option checked,
|
||||
disabled option st.disabled
|
||||
),
|
||||
label(`for` := id(field))
|
||||
cmnToggle(id(field), field.name, field.value.has("true"), disabled)
|
||||
),
|
||||
groupLabel(field)(labelContent)
|
||||
),
|
||||
help map { helper(_) }
|
||||
)
|
||||
|
||||
def cmnToggle(fieldId: String, fieldName: String, checked: Boolean, disabled: Boolean = false, value: String = "true") = frag(
|
||||
st.input(
|
||||
st.id := fieldId,
|
||||
name := fieldName,
|
||||
st.value := value,
|
||||
tpe := "checkbox",
|
||||
cls := "form-control cmn-toggle",
|
||||
checked option st.checked,
|
||||
disabled option st.disabled
|
||||
),
|
||||
label(`for` := fieldId)
|
||||
)
|
||||
|
||||
def select(
|
||||
field: Field,
|
||||
options: Iterable[(Any, String)],
|
||||
|
|
|
@ -206,8 +206,7 @@ object inquiry {
|
|||
div(cls := "actions close")(
|
||||
span(cls := "switcher", title := "Automatically open next report")(
|
||||
span(cls := "switch")(
|
||||
input(id := "auto-next", cls := "cmn-toggle", tpe := "checkbox", checked),
|
||||
label(`for` := "auto-next")
|
||||
form3.cmnToggle("auto-next", "auto-next", true)
|
||||
)
|
||||
),
|
||||
postForm(
|
||||
|
|
|
@ -41,17 +41,7 @@ object permissions {
|
|||
s"Granted by package: $p"
|
||||
}
|
||||
})(
|
||||
span(
|
||||
input(
|
||||
st.id := id,
|
||||
cls := "cmn-toggle",
|
||||
tpe := "checkbox",
|
||||
name := "permissions[]",
|
||||
value := perm.dbKey,
|
||||
u.roles.contains(perm.dbKey) option checked
|
||||
),
|
||||
label(`for` := id)
|
||||
),
|
||||
span(form3.cmnToggle(id, "permissions[]", checked = u.roles.contains(perm.dbKey), value = perm.dbKey)),
|
||||
label(`for` := id)(perm.name)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,17 +39,7 @@ object create {
|
|||
}
|
||||
val id = s"oauth-scope-${scope.key.replace(":", "_")}"
|
||||
div(
|
||||
span(
|
||||
input(
|
||||
st.id := id,
|
||||
cls := "cmn-toggle",
|
||||
tpe := "checkbox",
|
||||
name := s"${form("scopes").name}[]",
|
||||
value := scope.key,
|
||||
disabled option st.disabled
|
||||
),
|
||||
label(`for` := id)
|
||||
),
|
||||
span(form3.cmnToggle(id, s"${form("scopes").name}[]", value = scope.key, checked = false, disabled = disabled)),
|
||||
label(`for` := id, st.title := disabled.option("You already have played games!"))(scope.name)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -92,10 +92,7 @@ object bits {
|
|||
"round-toggle-autoswitch" |> { id =>
|
||||
span(cls := "move-on switcher", st.title := trans.automaticallyProceedToNextGameAfterMoving.txt())(
|
||||
label(`for` := id)(trans.autoSwitch()),
|
||||
span(cls := "switch")(
|
||||
input(st.id := id, cls := "cmn-toggle", tpe := "checkbox"),
|
||||
label(`for` := id)
|
||||
)
|
||||
span(cls := "switch")(form3.cmnToggle(id, id, false))
|
||||
)
|
||||
}
|
||||
),
|
||||
|
|
|
@ -175,15 +175,7 @@ private object bits {
|
|||
)
|
||||
),
|
||||
td(cls := "single")(
|
||||
st.input(
|
||||
tpe := "checkbox",
|
||||
cls := "cmn-toggle",
|
||||
id := form3.id(field),
|
||||
name := field.name,
|
||||
value := "1",
|
||||
field.value.has("1") option checked
|
||||
),
|
||||
label(`for` := form3.id(field))
|
||||
form3.cmnToggle(form3.id(field), field.name, checked = field.value.has("1"), value = "1")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -508,7 +508,7 @@ object mod {
|
|||
othersWithEmail.others.map {
|
||||
case lila.security.UserSpy.OtherUser(o, byIp, byFp) =>
|
||||
val dox = isGranted(_.Doxing) || (o.lameOrAlt && !o.hasTitle)
|
||||
val myNotes = notes.filter(_.to == o.id)
|
||||
val userNotes = notes.filter(n => n.to == o.id && (ctx.me.exists(n.isFrom) || isGranted(_.Doxing)))
|
||||
tr(o == u option (cls := "same"))(
|
||||
if (dox || o == u) td(dataSort := o.id)(userLink(o, withBestRating = true, params = "?mod"))
|
||||
else td,
|
||||
|
@ -527,14 +527,14 @@ object mod {
|
|||
markTd(o.marks.ipban ?? 1, ipban(cls := "is-red")),
|
||||
markTd(o.disabled ?? 1, closed),
|
||||
markTd(o.marks.reportban ?? 1, reportban),
|
||||
myNotes.nonEmpty option {
|
||||
td(dataSort := myNotes.size)(
|
||||
userNotes.nonEmpty option {
|
||||
td(dataSort := userNotes.size)(
|
||||
a(href := s"${routes.User.show(o.username)}?notes")(
|
||||
notesText(
|
||||
title := s"Notes from ${myNotes.map(_.from).map(usernameOrId).mkString(", ")}",
|
||||
title := s"Notes from ${userNotes.map(_.from).map(usernameOrId).mkString(", ")}",
|
||||
cls := "is-green"
|
||||
),
|
||||
myNotes.size
|
||||
userNotes.size
|
||||
)
|
||||
)
|
||||
} getOrElse td(dataSort := 0),
|
||||
|
|
|
@ -128,29 +128,32 @@ object header {
|
|||
postForm(action := s"${routes.User.writeNote(u.username)}?note")(
|
||||
textarea(
|
||||
name := "text",
|
||||
placeholder := "Write a note about this user only you and your friends can read"
|
||||
placeholder := "Write a private note about this user"
|
||||
),
|
||||
submitButton(cls := "button")(trans.send()),
|
||||
if (isGranted(_.ModNote))
|
||||
label(style := "margin-left: 1em;")(
|
||||
input(
|
||||
tpe := "checkbox",
|
||||
name := "mod",
|
||||
checked,
|
||||
value := "true",
|
||||
style := "vertical-align: middle;"
|
||||
),
|
||||
"For moderators only"
|
||||
if (isGranted(_.ModNote)) div(cls := "mod-note")(
|
||||
submitButton(cls := "button")(trans.send()),
|
||||
div(
|
||||
div(form3.cmnToggle("note-mod", "mod", true)),
|
||||
label(`for` := "note-mod")("For moderators only")
|
||||
),
|
||||
isGranted(_.Doxing) option div(
|
||||
div(form3.cmnToggle("note-dox", "dox", false)),
|
||||
label(`for` := "note-dox")("Doxing info")
|
||||
)
|
||||
else input(tpe := "hidden", name := "mod", value := "false")
|
||||
)
|
||||
else frag(
|
||||
input(tpe := "hidden", name := "mod", value := "false"),
|
||||
submitButton(cls := "button")(trans.send()),
|
||||
)
|
||||
),
|
||||
social.notes.isEmpty option div("No note yet"),
|
||||
social.notes.map { note =>
|
||||
social.notes.filter(n => ctx.me.exists(n.isFrom) || isGranted(_.Doxing)).map { note =>
|
||||
div(cls := "note")(
|
||||
p(cls := "note__text")(richText(note.text)),
|
||||
p(cls := "note__meta")(
|
||||
userIdLink(note.from.some),
|
||||
br,
|
||||
note.dox option "dox ",
|
||||
momentFromNow(note.date),
|
||||
(ctx.me.exists(note.isFrom) && !note.mod) option frag(
|
||||
br,
|
||||
|
|
|
@ -83,7 +83,8 @@ object Permission {
|
|||
SeeInsight,
|
||||
UserSearch,
|
||||
RemoveRanking,
|
||||
ModMessage
|
||||
ModMessage,
|
||||
ModNote
|
||||
),
|
||||
"Hunter"
|
||||
)
|
||||
|
@ -92,7 +93,6 @@ object Permission {
|
|||
extends Permission(
|
||||
"DOXING",
|
||||
List(
|
||||
ModNote,
|
||||
ViewIpPrint
|
||||
),
|
||||
"Doxing"
|
||||
|
|
|
@ -11,11 +11,12 @@ final class DataForm(authenticator: Authenticator) {
|
|||
val note = Form(
|
||||
mapping(
|
||||
"text" -> text(minLength = 3, maxLength = 2000),
|
||||
"mod" -> boolean
|
||||
"mod" -> boolean,
|
||||
"dox" -> optional(boolean)
|
||||
)(NoteData.apply)(NoteData.unapply)
|
||||
)
|
||||
|
||||
case class NoteData(text: String, mod: Boolean)
|
||||
case class NoteData(text: String, mod: Boolean, dox: Option[Boolean])
|
||||
|
||||
def username(user: User): Form[String] =
|
||||
Form(
|
||||
|
|
|
@ -9,6 +9,7 @@ case class Note(
|
|||
to: User.ID,
|
||||
text: String,
|
||||
mod: Boolean,
|
||||
dox: Boolean,
|
||||
date: DateTime
|
||||
) {
|
||||
def userIds = List(from, to)
|
||||
|
@ -50,7 +51,7 @@ final class NoteApi(
|
|||
.sort($sort desc "date")
|
||||
.list[Note](50)
|
||||
|
||||
def write(to: User, text: String, from: User, modOnly: Boolean) = {
|
||||
def write(to: User, text: String, from: User, modOnly: Boolean, dox: Boolean) = {
|
||||
|
||||
val note = Note(
|
||||
_id = ornicar.scalalib.Random nextString 8,
|
||||
|
@ -58,6 +59,7 @@ final class NoteApi(
|
|||
to = to.id,
|
||||
text = text,
|
||||
mod = modOnly,
|
||||
dox = modOnly && dox,
|
||||
date = DateTime.now
|
||||
)
|
||||
|
||||
|
@ -85,7 +87,7 @@ final class NoteApi(
|
|||
def lichessWrite(to: User, text: String) =
|
||||
userRepo.lichess flatMap {
|
||||
_ ?? {
|
||||
write(to, text, _, true)
|
||||
write(to, text, _, true, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
@import '../../../common/css/component/hover-text';
|
||||
@import '../../../common/css/component/crosstable';
|
||||
@import '../../../common/css/component/flash';
|
||||
@import '../../../common/css/form/cmn-toggle';
|
||||
@import '../../../common/css/base/scrollbar';
|
||||
@import '../../../game/css/row';
|
||||
@import '../user/show';
|
||||
|
|
|
@ -21,4 +21,15 @@
|
|||
min-height: 2.7em;
|
||||
}
|
||||
}
|
||||
.mod-note {
|
||||
@extend %flex-center;
|
||||
> div {
|
||||
@extend %flex-center;
|
||||
margin-left: 1.5em;
|
||||
> label {
|
||||
margin-left: .5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue