Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 32 additions & 16 deletions modules/common/src/main/Form.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,34 +78,50 @@ object Form:
val cleanText: Mapping[String] = of(cleanTextFormatter)
val cleanTextWithSymbols: Mapping[String] = of(cleanTextFormatterWithSymbols)

def cleanText(minLength: Int = 0, maxLength: Int = Int.MaxValue): Mapping[String] =
private def addLengthConstraints(m: Mapping[String], minLength: Int, maxLength: Int) =
(minLength, maxLength) match
case (min, Int.MaxValue) => cleanText.verifying(Constraints.minLength(min))
case (0, max) => cleanText.verifying(Constraints.maxLength(max))
case (min, max) => cleanText.verifying(Constraints.minLength(min), Constraints.maxLength(max))
case (min, Int.MaxValue) => m.verifying(Constraints.minLength(min))
case (0, max) => m.verifying(Constraints.maxLength(max))
case (min, max) => m.verifying(Constraints.minLength(min), Constraints.maxLength(max))

def cleanText(minLength: Int = 0, maxLength: Int = Int.MaxValue): Mapping[String] =
addLengthConstraints(cleanText, minLength, maxLength)

val cleanNonEmptyText: Mapping[String] = cleanText.verifying(Constraints.nonEmpty)
def cleanNonEmptyText(minLength: Int = 0, maxLength: Int = Int.MaxValue): Mapping[String] =
cleanText(minLength, maxLength).verifying(Constraints.nonEmpty)

def cleanTextWithSymbols(minLength: Int = 0, maxLength: Int = Int.MaxValue): Mapping[String] =
addLengthConstraints(cleanTextWithSymbols, minLength, maxLength)

def cleanNoSymbolsText(minLength: Int = 0, maxLength: Int = Int.MaxValue): Mapping[String] =
cleanTextWithSymbols(minLength, maxLength).verifying(noSymbolsConstraint)

def cleanNoSymbolsAndNonEmptyText(minLength: Int = 0, maxLength: Int = Int.MaxValue): Mapping[String] =
cleanNoSymbolsText(minLength, maxLength).verifying(Constraints.nonEmpty)

private val eventNameConstraint = Constraints.pattern(
regex = """[\p{L}\p{N}-\s:.,;'°ª\+]+""".r,
error = "Invalid characters; only letters, numbers, and common punctuation marks are accepted."
)

private val symbolsRegex =
raw"[\p{So}\p{block=Emoticons}\p{block=Miscellaneous Symbols and Pictographs}\p{block=Supplemental Symbols and Pictographs}]".r
val noSymbolsConstraint = V.Constraint[String]: t =>
if symbolsRegex.find(t) then V.Invalid(V.ValidationError("Must not contain emojis or other symbols"))
else V.Valid

val slugConstraint: V.Constraint[String] =
Constraints.pattern(
regex = """[\w-]+""".r,
error = "Invalid characters; only letters, numbers, and dashes are accepted."
)

object eventName:

def apply(minLength: Int, maxLength: Int, verifiedUser: Boolean) =
cleanText.verifying(
Constraints.minLength(minLength),
Constraints.maxLength(maxLength),
Constraints.pattern(
regex = """[\p{L}\p{N}-\s:.,;'°ª\+]+""".r,
error = "Invalid characters; only letters, numbers, and common punctuation marks are accepted."
),
mustNotContainLichess(verifiedUser)
)
def eventName(minLength: Int, maxLength: Int, verifiedUser: Boolean) =
addLengthConstraints(cleanText, minLength, maxLength).verifying(
eventNameConstraint,
mustNotContainLichess(verifiedUser)
)

object mustNotContainLichess:
// \u0131\u0307 is ı (\u0131) with an i dot (\u0307)
Expand Down
18 changes: 13 additions & 5 deletions modules/user/src/main/UserForm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import play.api.data.*
import play.api.data.Forms.*
import play.api.data.validation.Constraints

import lila.common.Form.{ cleanNonEmptyText, cleanText, into, trim, given }
import lila.common.Form.{
cleanNonEmptyText,
cleanText,
cleanNoSymbolsText,
cleanNoSymbolsAndNonEmptyText,
into,
trim,
given
}
import lila.common.LameName
import lila.core.user.Profile

Expand All @@ -31,8 +39,8 @@ final class UserForm:
val profile: Form[Profile] = Form:
mapping(
"flag" -> optional(text.verifying(Flags.codeSet contains _)),
"location" -> optional(cleanNonEmptyText(maxLength = 80)),
"bio" -> optional(cleanNonEmptyText(maxLength = 400)),
"location" -> optional(cleanNoSymbolsAndNonEmptyText(maxLength = 80)),
"bio" -> optional(cleanNoSymbolsAndNonEmptyText(maxLength = 400)),
"firstName" -> nameField,
"lastName" -> nameField,
"fideRating" -> optional(number(min = 1400, max = 3000)),
Expand All @@ -41,15 +49,15 @@ final class UserForm:
"rcfRating" -> optional(number(min = 0, max = 3000)),
"cfcRating" -> optional(number(min = 0, max = 3000)),
"dsbRating" -> optional(number(min = 0, max = 3000)),
"links" -> optional(cleanNonEmptyText(maxLength = 3000))
"links" -> optional(cleanNoSymbolsAndNonEmptyText(maxLength = 3000))
)(Profile.apply)(unapply)

def profileOf(user: User) = profile.fill(user.profileOrDefault)

def flair(using Me) = Form[Option[Flair]]:
single(FlairApi.formPair())

private def nameField = optional(cleanText(minLength = 1, maxLength = 20))
private def nameField = optional(cleanNoSymbolsText(minLength = 1, maxLength = 20))

object UserForm:

Expand Down