class student real name WIP

This commit is contained in:
Thibault Duplessis 2020-01-17 16:00:58 -06:00
parent 104b7d99a0
commit 9fd53a3c9e
7 changed files with 58 additions and 36 deletions

View file

@ -104,8 +104,8 @@ final class Clas(
err err
) )
).fuccess, ).fuccess,
username => data =>
env.clas.api.student.create(clas, username, t) map { env.clas.api.student.create(clas, data, t) map {
case (user, password) => case (user, password) =>
Redirect(routes.Clas.studentShow(clas.id.value, user.username)) Redirect(routes.Clas.studentShow(clas.id.value, user.username))
.flashing("password" -> password.value) .flashing("password" -> password.value)
@ -129,8 +129,8 @@ final class Clas(
env.clas.forms.student.create env.clas.forms.student.create
) )
).fuccess, ).fuccess,
username => data =>
env.user.repo named username flatMap { env.user.repo named data.username flatMap {
_ ?? { user => _ ?? { user =>
env.clas.api.student.invite(clas, user, t) inject env.clas.api.student.invite(clas, user, t) inject
Redirect(routes.Clas.studentForm(clas.id.value)).flashSuccess Redirect(routes.Clas.studentForm(clas.id.value)).flashSuccess

View file

@ -6,7 +6,7 @@ import lila.api.Context
import lila.app.templating.Environment._ import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._ import lila.app.ui.ScalatagsTemplate._
import lila.clas.{ Clas, Student } import lila.clas.{ Clas, Student }
import lila.clas.ClasForm.Data import lila.clas.ClasForm.ClasData
import controllers.routes import controllers.routes
object clas { object clas {
@ -92,21 +92,21 @@ object clas {
fragList(clas.teachers.toList.map(t => userIdLink(t.value.some))) fragList(clas.teachers.toList.map(t => userIdLink(t.value.some)))
) )
def create(form: Form[Data])(implicit ctx: Context) = def create(form: Form[ClasData])(implicit ctx: Context) =
bits.layout("New class", Right("newClass"))( bits.layout("New class", Right("newClass"))(
cls := "box-pad", cls := "box-pad",
h1("New class"), h1("New class"),
innerForm(form, routes.Clas.create) innerForm(form, routes.Clas.create)
) )
def edit(c: lila.clas.Clas, form: Form[Data])(implicit ctx: Context) = def edit(c: lila.clas.Clas, form: Form[ClasData])(implicit ctx: Context) =
bits.layout(c.name, Left(c))( bits.layout(c.name, Left(c))(
cls := "box-pad", cls := "box-pad",
h1("Edit ", c.name), h1("Edit ", c.name),
innerForm(form, routes.Clas.update(c.id.value)) innerForm(form, routes.Clas.update(c.id.value))
) )
private def innerForm(form: Form[Data], url: play.api.mvc.Call)(implicit ctx: Context) = private def innerForm(form: Form[ClasData], url: play.api.mvc.Call)(implicit ctx: Context) =
postForm(cls := "form3", action := url)( postForm(cls := "form3", action := url)(
form3.globalError(form), form3.globalError(form),
form3.group(form("name"), frag("Class name"))(form3.input(_)(autofocus)), form3.group(form("name"), frag("Class name"))(form3.input(_)(autofocus)),

View file

@ -142,7 +142,14 @@ object student {
) )
) )
def form(c: lila.clas.Clas, invite: Form[String], create: Form[String])(implicit ctx: Context) = private def realName(form: Form[_])(implicit ctx: Context) =
form3.group(
form("realName"),
frag("Real name"),
help = frag("Private info, never visible on Lichess. Helps you remember who that student is.").some
)(form3.input(_))
def form(c: lila.clas.Clas, invite: Form[_], create: Form[_])(implicit ctx: Context) =
bits.layout("Add student", Left(c))( bits.layout("Add student", Left(c))(
cls := "box-pad student-add", cls := "box-pad student-add",
h1("Add student"), h1("Add student"),
@ -166,6 +173,7 @@ object student {
form3.group(invite("invite"), frag("Invite username"))( form3.group(invite("invite"), frag("Invite username"))(
form3.input(_, klass = "user-autocomplete")(autofocus)(dataTag := "span") form3.input(_, klass = "user-autocomplete")(autofocus)(dataTag := "span")
), ),
realName(invite),
form3.submit("Invite") form3.submit("Invite")
) )
), ),
@ -186,6 +194,7 @@ object student {
), ),
postForm(cls := "form3", action := routes.Clas.studentCreate(c.id.value))( postForm(cls := "form3", action := routes.Clas.studentCreate(c.id.value))(
form3.group(create("username"), frag("Create username"))(form3.input(_)(autofocus)), form3.group(create("username"), frag("Create username"))(form3.input(_)(autofocus)),
realName(create),
form3.submit(trans.signUp()) form3.submit(trans.signUp())
) )
) )

View file

@ -50,12 +50,12 @@ final class ClasApi(
.sort($sort desc "viewedAt") .sort($sort desc "viewedAt")
.list[Clas]() .list[Clas]()
def create(data: ClasForm.Data, teacher: Teacher): Fu[Clas] = { def create(data: ClasForm.ClasData, teacher: Teacher): Fu[Clas] = {
val clas = Clas.make(teacher, data.name, data.desc) val clas = Clas.make(teacher, data.name, data.desc)
coll.insert.one(clas) inject clas coll.insert.one(clas) inject clas
} }
def update(from: Clas, data: ClasForm.Data): Fu[Clas] = { def update(from: Clas, data: ClasForm.ClasData): Fu[Clas] = {
val clas = data update from val clas = data update from
coll.update.one($id(clas.id), clas) inject clas coll.update.one($id(clas.id), clas) inject clas
} }
@ -109,23 +109,27 @@ final class ClasApi(
def isIn(clas: Clas, userId: User.ID): Fu[Boolean] = def isIn(clas: Clas, userId: User.ID): Fu[Boolean] =
coll.exists($id(Student.id(userId, clas.id))) coll.exists($id(Student.id(userId, clas.id)))
def create(clas: Clas, username: String, teacher: Teacher.WithUser): Fu[(User, ClearPassword)] = { def create(
val email = EmailAddress(s"noreply.class.${clas.id}.$username@lichess.org") clas: Clas,
data: ClasForm.NewStudent,
teacher: Teacher.WithUser
): Fu[(User, ClearPassword)] = {
val email = EmailAddress(s"noreply.class.${clas.id}.${data.username}@lichess.org")
val password = Student.password.generate val password = Student.password.generate
lila.mon.clas.studentCreate(teacher.user.id) lila.mon.clas.studentCreate(teacher.user.id)
userRepo userRepo
.create( .create(
username = username, username = data.username,
passwordHash = authenticator.passEnc(password), passwordHash = authenticator.passEnc(password),
email = email, email = email,
blind = false, blind = false,
mobileApiVersion = none, mobileApiVersion = none,
mustConfirmEmail = false mustConfirmEmail = false
) )
.orFail(s"No user could be created for $username") .orFail(s"No user could be created for ${data.username}")
.flatMap { user => .flatMap { user =>
userRepo.setKid(user, true) >> userRepo.setKid(user, true) >>
coll.insert.one(Student.make(user, clas, teacher.teacher.id, managed = true)) >> coll.insert.one(Student.make(user, clas, teacher.teacher.id, data.realName, managed = true)) >>
sendWelcomeMessage(teacher, user, clas, s"$baseUrl/class/${clas.id}") inject sendWelcomeMessage(teacher, user, clas, s"$baseUrl/class/${clas.id}") inject
(user -> password) (user -> password)
} }
@ -139,7 +143,7 @@ final class ClasApi(
} }
private[ClasApi] def join(clas: Clas, user: User, teacherId: Teacher.Id): Fu[Student] = { private[ClasApi] def join(clas: Clas, user: User, teacherId: Teacher.Id): Fu[Student] = {
val student = Student.make(user, clas, teacherId, managed = false) val student = Student.make(user, clas, teacherId, "", managed = false)
coll.insert.one(student) inject student coll.insert.one(student) inject student
} }

View file

@ -15,26 +15,32 @@ final class ClasForm(
mapping( mapping(
"name" -> text(minLength = 3, maxLength = 100), "name" -> text(minLength = 3, maxLength = 100),
"desc" -> text(minLength = 0, maxLength = 2000) "desc" -> text(minLength = 0, maxLength = 2000)
)(Data.apply)(Data.unapply) )(ClasData.apply)(ClasData.unapply)
) )
def create = form def create = form
def edit(c: Clas) = form fill Data( def edit(c: Clas) = form fill ClasData(
name = c.name, name = c.name,
desc = c.desc desc = c.desc
) )
object student { object student {
def create = securityForms.signup.managed def create = Form(
mapping(
"username" -> securityForms.signup.username,
"realName" -> nonEmptyText
)(NewStudent.apply)(NewStudent.unapply)
)
def invite = Form( def invite = Form(
single( mapping(
"invite" -> lila.user.DataForm.historicalUsernameField.verifying("Unknown username", { "username" -> lila.user.DataForm.historicalUsernameField.verifying("Unknown username", {
blockingFetchUser(_).isDefined blockingFetchUser(_).isDefined
}) }),
) "realName" -> nonEmptyText
)(NewStudent.apply)(NewStudent.unapply)
) )
private def blockingFetchUser(username: String) = private def blockingFetchUser(username: String) =
@ -44,7 +50,7 @@ final class ClasForm(
object ClasForm { object ClasForm {
case class Data( case class ClasData(
name: String, name: String,
desc: String desc: String
) { ) {
@ -53,4 +59,9 @@ object ClasForm {
desc = desc desc = desc
) )
} }
case class NewStudent(
username: String,
realName: String
)
} }

View file

@ -8,6 +8,8 @@ case class Student(
_id: Student.Id, // userId:clasId _id: Student.Id, // userId:clasId
userId: User.ID, userId: User.ID,
clasId: Clas.Id, clasId: Clas.Id,
realName: String,
notes: String,
managed: Boolean, // created for the class by the teacher managed: Boolean, // created for the class by the teacher
created: Clas.Recorded, created: Clas.Recorded,
archived: Option[Clas.Recorded] archived: Option[Clas.Recorded]
@ -27,10 +29,12 @@ object Student {
def id(userId: User.ID, clasId: Clas.Id) = Id(s"${userId}:${clasId}") def id(userId: User.ID, clasId: Clas.Id) = Id(s"${userId}:${clasId}")
def make(user: User, clas: Clas, teacherId: Teacher.Id, managed: Boolean) = Student( def make(user: User, clas: Clas, teacherId: Teacher.Id, realName: String, managed: Boolean) = Student(
_id = id(user.id, clas.id), _id = id(user.id, clas.id),
userId = user.id, userId = user.id,
clasId = clas.id, clasId = clas.id,
realName = realName,
notes = "",
managed = managed, managed = managed,
created = Clas.Recorded(teacherId, DateTime.now), created = Clas.Recorded(teacherId, DateTime.now),
archived = none archived = none

View file

@ -52,7 +52,7 @@ final class DataForm(
object signup { object signup {
private val username = trimField(nonEmptyText) val username = trimField(nonEmptyText)
.verifying( .verifying(
Constraints minLength 2, Constraints minLength 2,
Constraints maxLength 20, Constraints maxLength 20,
@ -83,7 +83,7 @@ final class DataForm(
val website = Form( val website = Form(
mapping( mapping(
"username" -> trimField(username), "username" -> username,
"password" -> text(minLength = 4), "password" -> text(minLength = 4),
"email" -> withAcceptableDns(acceptableUniqueEmail(none)), "email" -> withAcceptableDns(acceptableUniqueEmail(none)),
"agreement" -> agreement, "agreement" -> agreement,
@ -94,17 +94,11 @@ final class DataForm(
val mobile = Form( val mobile = Form(
mapping( mapping(
"username" -> trimField(username), "username" -> username,
"password" -> text(minLength = 4), "password" -> text(minLength = 4),
"email" -> withAcceptableDns(acceptableUniqueEmail(none)) "email" -> withAcceptableDns(acceptableUniqueEmail(none))
)(MobileSignupData.apply)(_ => None) )(MobileSignupData.apply)(_ => None)
) )
val managed = Form(
single(
"username" -> trimField(username)
)
)
} }
val passwordReset = Form( val passwordReset = Form(
@ -214,7 +208,7 @@ final class DataForm(
val reopen = Form( val reopen = Form(
mapping( mapping(
"username" -> nonEmptyText, "username" -> trimField(nonEmptyText),
"email" -> sendableEmail, // allow unacceptable emails for BC "email" -> sendableEmail, // allow unacceptable emails for BC
"gameId" -> text, "gameId" -> text,
"move" -> text "move" -> text