class news WIP

pull/5979/head
Thibault Duplessis 2020-01-30 13:19:24 -06:00
parent a5a9c23829
commit ed5e69cf67
12 changed files with 171 additions and 28 deletions

View File

@ -46,12 +46,12 @@ final class Clas(
}
def form = Secure(_.Teacher) { implicit ctx => _ =>
Ok(html.clas.clas.create(env.clas.forms.create)).fuccess
Ok(html.clas.clas.create(env.clas.forms.clas.create)).fuccess
}
def create = SecureBody(_.Teacher) { implicit ctx => me =>
WithTeacher(me) { t =>
env.clas.forms.create
env.clas.forms.clas.create
.bindFromRequest()(ctx.body)
.fold(
err => BadRequest(html.clas.clas.create(err)).fuccess,
@ -91,6 +91,39 @@ final class Clas(
}
}
def wall(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas =>
env.clas.api.student.allWithUsers(clas) map { students =>
val wall = scalatags.Text.all.raw(env.clas.markup(clas.wall))
views.html.clas.wall.show(clas, wall, students)
}
}
}
def wallEdit(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas =>
env.clas.api.student.activeWithUsers(clas) map { students =>
Ok(html.clas.wall.edit(clas, students, env.clas.forms.clas.wall fill clas.wall))
}
}
}
def wallUpdate(id: String) = SecureBody(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas =>
env.clas.forms.clas.wall
.bindFromRequest()(ctx.body)
.fold(
err =>
env.clas.api.student.activeWithUsers(clas) map { students =>
BadRequest(html.clas.wall.edit(clas, students, err))
},
text =>
env.clas.api.clas.updateWall(clas, text) inject
Redirect(routes.Clas.wall(clas.id.value)).flashSuccess
)
}
}
def archived(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas =>
env.clas.api.student.allWithUsers(clas) map { students =>
@ -114,14 +147,14 @@ final class Clas(
def edit(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas =>
env.clas.api.student.activeWithUsers(clas) map { students =>
Ok(html.clas.clas.edit(clas, students, env.clas.forms.edit(clas)))
Ok(html.clas.clas.edit(clas, students, env.clas.forms.clas.edit(clas)))
}
}
}
def update(id: String) = SecureBody(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas =>
env.clas.forms
env.clas.forms.clas
.edit(clas)
.bindFromRequest()(ctx.body)
.fold(

View File

@ -32,6 +32,7 @@ object teacherDashboard {
),
st.nav(cls := "dashboard-nav")(
a(cls := active.active("overview"), href := routes.Clas.show(c.id.value))("Overview"),
a(cls := active.active("wall"), href := routes.Clas.wall(c.id.value))("News"),
a(
cls := active.active("progress"),
href := routes.Clas.progress(c.id.value, PerfType.Blitz.key, 7)

View File

@ -0,0 +1,46 @@
package views.html.clas
import play.api.data.Form
import controllers.routes
import lila.api.Context
import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._
import lila.clas.{ Clas, Student }
object wall {
def show(
c: Clas,
html: Frag,
students: List[Student.WithUser]
)(implicit ctx: Context) =
teacherDashboard.layout(c, students.filter(_.student.isActive), "wall") {
if (c.wall.isEmpty)
div(cls := "box__pad clas-wall clas-wall--empty")("Nothing here, yet.")
else
div(cls := "box__pad clas-wall")(html)
}
def edit(
c: Clas,
students: List[Student.WithUser],
form: Form[_]
)(implicit ctx: Context) =
teacherDashboard.layout(c, students, "wall")(
div(cls := "box-pad")(
postForm(cls := "form3", action := routes.Clas.wallUpdate(c.id.value))(
form3.globalError(form),
form3.group(
form("wall"),
frag("Class news"),
help = frag("Add the most recent news at the top.").some
)(form3.textarea(_)(rows := 20)),
form3.actions(
a(href := routes.Clas.show(c.id.value))(trans.cancel()),
form3.submit(trans.apply())
)
)
)
)
}

View File

@ -375,7 +375,7 @@ lazy val teamSearch = module("teamSearch",
lazy val clas = module("clas",
Seq(common, memo, db, user, security, msg, history, puzzle),
reactivemongo.bundle
Seq(markdown) ++ reactivemongo.bundle
)
lazy val bookmark = module("bookmark",

View File

@ -446,6 +446,9 @@ GET /class/verify-teacher controllers.Clas.verifyTeacher
GET /class/$id<\w{8}> controllers.Clas.show(id: String)
GET /class/$id<\w{8}>/edit controllers.Clas.edit(id: String)
POST /class/$id<\w{8}>/edit controllers.Clas.update(id: String)
GET /class/$id<\w{8}>/news controllers.Clas.wall(id: String)
GET /class/$id<\w{8}>/news/edit controllers.Clas.wallEdit(id: String)
POST /class/$id<\w{8}>/news/edit controllers.Clas.wallUpdate(id: String)
POST /class/$id<\w{8}>/archive controllers.Clas.archive(id: String, v: Boolean)
GET /class/$id<\w{8}>/archived controllers.Clas.archived(id: String)
GET /class/$id<\w{8}>/progress/:pt/:days controllers.Clas.progress(id: String, pt: String, days: Int)

View File

@ -7,6 +7,7 @@ case class Clas(
_id: Clas.Id,
name: String,
desc: String,
wall: String = "",
teachers: NonEmptyList[Teacher.Id], // first is owner
created: Clas.Recorded,
viewedAt: DateTime,

View File

@ -82,6 +82,9 @@ final class ClasApi(
}
}
def updateWall(clas: Clas, text: String): Funit =
coll.updateField($id(clas.id), "wall", text).void
def getAndView(id: Clas.Id, teacher: Teacher): Fu[Option[Clas]] =
coll.ext
.findAndUpdate[Clas](

View File

@ -13,26 +13,32 @@ final class ClasForm(
import ClasForm._
val form = Form(
mapping(
"name" -> text(minLength = 3, maxLength = 100),
"desc" -> text(minLength = 0, maxLength = 2000),
"teachers" -> nonEmptyText.verifying("Invalid teacher list", str => {
val ids = readTeacherIds(str)
ids.nonEmpty && ids.size <= 10 && ids.forall { id =>
blockingFetchUser(id.value).isDefined
}
})
)(ClasData.apply)(ClasData.unapply)
)
object clas {
def create = form
val form = Form(
mapping(
"name" -> text(minLength = 3, maxLength = 100),
"desc" -> text(minLength = 0, maxLength = 2000),
"teachers" -> nonEmptyText.verifying("Invalid teacher list", str => {
val ids = readTeacherIds(str)
ids.nonEmpty && ids.size <= 10 && ids.forall { id =>
blockingFetchUser(id.value).isDefined
}
})
)(ClasData.apply)(ClasData.unapply)
)
def edit(c: Clas) = form fill ClasData(
name = c.name,
desc = c.desc,
teachers = c.teachers.toList mkString "\n"
)
def create = form
def edit(c: Clas) = form fill ClasData(
name = c.name,
desc = c.desc,
teachers = c.teachers.toList mkString "\n"
)
def wall =
Form(single("wall" -> text))
}
object student {

View File

@ -0,0 +1,41 @@
package lila.clas
import com.github.blemale.scaffeine.Cache
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
import com.vladsch.flexmark.ext.tables.TablesExtension
import com.vladsch.flexmark.ext.autolink.AutolinkExtension
import com.vladsch.flexmark.html.HtmlRenderer
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.util.data.MutableDataSet
import java.util.Arrays
import scala.concurrent.duration._
final class ClasMarkup {
type Text = String
type Html = String
private val options = new MutableDataSet()
options.set(
Parser.EXTENSIONS,
Arrays.asList(
TablesExtension.create(),
StrikethroughExtension.create(),
AutolinkExtension.create()
)
)
options.set(HtmlRenderer.SOFT_BREAK, "<br>\n")
options.set(TablesExtension.CLASS_NAME, "slist")
private val parser = Parser.builder(options).build()
private val renderer = HtmlRenderer.builder(options).build()
private val cache: Cache[Text, Html] = lila.memo.CacheApi.scaffeineNoScheduler
.expireAfterAccess(10 minutes)
.maximumSize(64)
.build[Text, Html]
private def compute(text: Text): Html =
renderer.render(parser.parse(text))
def apply(text: Text): Html = cache.get(text, compute)
}

View File

@ -31,6 +31,8 @@ final class Env(
lazy val progressApi = wire[ClasProgressApi]
lazy val markup = wire[ClasMarkup]
lila.common.Bus.subscribeFuns(
"finishGame" -> {
case lila.game.actorApi.FinishGame(game, _, _) => progressApi.onFinishGame(game)

View File

@ -16,7 +16,7 @@ final private class RelayMarkup {
private val options = new MutableDataSet()
options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create(), StrikethroughExtension.create()))
options.set(HtmlRenderer.SOFT_BREAK, "<br />\n")
options.set(HtmlRenderer.SOFT_BREAK, "<br>\n")
options.set(TablesExtension.CLASS_NAME, "slist")
private val parser = Parser.builder(options).build()
private val renderer = HtmlRenderer.builder(options).build()

View File

@ -101,6 +101,7 @@ $c-bg-clas-over-dim: mix($c-bg-clas-over, $c-bg-clas, 80%);
}
.clas-show {
@extend %box-neat-force;
&__top {
background: $c-bg-clas;
@ -190,12 +191,12 @@ $c-bg-clas-over-dim: mix($c-bg-clas-over, $c-bg-clas, 80%);
background: mix($c-bg-zebra, $c-clas, 20%);
}
&.active {
background: $c-bg-zebra;
background: $c-bg-box;
color: $c-font-clas;
font-weight: bold;
.dashboard-teacher-edit &,
.dashboard-teacher-archived & {
background: $c-bg-box;
.dashboard-teacher-overview &,
.dashboard-teacher-progress & {
background: $c-bg-zebra;
}
}
}
@ -280,6 +281,12 @@ $c-bg-clas-over-dim: mix($c-bg-clas-over, $c-bg-clas, 80%);
}
}
.clas-wall {
margin: 4em 0;
font-size: 1.1em;
line-height: 1.6em;
}
.student-show {
padding-bottom: 3em;