diff --git a/modules/common/src/main/Form.scala b/modules/common/src/main/Form.scala index 425a6a84c872..012e06bc04f6 100644 --- a/modules/common/src/main/Form.scala +++ b/modules/common/src/main/Form.scala @@ -100,11 +100,19 @@ object Form: 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(nonEmptyOrSpace) + def cleanFewSymbolsText( + minLength: Int = 0, + maxLength: Int = Int.MaxValue, + maxSymbols: Int = 0 + ): Mapping[String] = + cleanTextWithSymbols(minLength, maxLength).verifying(fewSymbolsConstraint(maxSymbols)) + + def cleanFewSymbolsAndNonEmptyText( + minLength: Int = 0, + maxLength: Int = Int.MaxValue, + maxSymbols: Int = 0 + ): Mapping[String] = + cleanFewSymbolsText(minLength, maxLength, maxSymbols).verifying(nonEmptyOrSpace) private val eventNameConstraint = Constraints.pattern( regex = """[\p{L}\p{N}-\s:.,;'°ª\+]+""".r, @@ -113,9 +121,14 @@ object Form: 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")) + + def fewSymbolsConstraint(maxSymbols: Int): V.Constraint[String] = V.Constraint[String] { t => + if symbolsRegex.findAllMatchIn(t).size > maxSymbols then + V.Invalid( + V.ValidationError(s"Must not contain more than $maxSymbols emojis or other symbols") + ) else V.Valid + } val slugConstraint: V.Constraint[String] = Constraints.pattern( diff --git a/modules/user/src/main/UserForm.scala b/modules/user/src/main/UserForm.scala index 5c10572608f9..38fd691538d7 100644 --- a/modules/user/src/main/UserForm.scala +++ b/modules/user/src/main/UserForm.scala @@ -3,8 +3,8 @@ import play.api.data.* import play.api.data.Forms.* import lila.common.Form.{ - cleanNoSymbolsAndNonEmptyText, - cleanNoSymbolsText, + cleanFewSymbolsAndNonEmptyText, + cleanFewSymbolsText, cleanNonEmptyText, cleanTextWithSymbols, into, @@ -35,16 +35,16 @@ final class UserForm: val profile: Form[Profile] = Form: mapping( "flag" -> optional(text.verifying(Flags.codeSet contains _)), - "location" -> optional(cleanNoSymbolsAndNonEmptyText(maxLength = 80)), - "bio" -> optional(cleanNoSymbolsAndNonEmptyText(maxLength = 400)), - "realName" -> optional(cleanNoSymbolsText(minLength = 1, maxLength = 100)), + "location" -> optional(cleanFewSymbolsAndNonEmptyText(maxLength = 80)), + "bio" -> optional(cleanFewSymbolsAndNonEmptyText(maxLength = 400, maxSymbols = 10)), + "realName" -> optional(cleanFewSymbolsText(minLength = 1, maxLength = 100)), "fideRating" -> optional(number(min = 1400, max = 3000)), "uscfRating" -> optional(number(min = 100, max = 3000)), "ecfRating" -> optional(number(min = 0, max = 3000)), "rcfRating" -> optional(number(min = 0, max = 3000)), "cfcRating" -> optional(number(min = 0, max = 3000)), "dsbRating" -> optional(number(min = 0, max = 3000)), - "links" -> optional(cleanNoSymbolsAndNonEmptyText(maxLength = 3000)) + "links" -> optional(cleanFewSymbolsAndNonEmptyText(maxLength = 3000)) )(Profile.apply)(unapply) def profileOf(user: User) = profile.fill(user.profileOrDefault)