refactor embeds, use no ctx, manual csp nonce
parent
c102071217
commit
791d140e4c
|
@ -68,16 +68,16 @@ object Analyse extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def embed(gameId: String, color: String) = Open { implicit ctx =>
|
||||
def embed(gameId: String, color: String) = Action.async { implicit req =>
|
||||
GameRepo.gameWithInitialFen(gameId) flatMap {
|
||||
case Some((game, initialFen)) =>
|
||||
val pov = Pov(game, chess.Color(color == "white"))
|
||||
Env.api.roundApi.review(pov, lila.api.Mobile.Api.currentVersion,
|
||||
Env.api.roundApi.embed(pov, lila.api.Mobile.Api.currentVersion,
|
||||
initialFenO = initialFen.some,
|
||||
withFlags = WithFlags(opening = true)) map { data =>
|
||||
Ok(html.analyse.embed(pov, data))
|
||||
}
|
||||
case _ => fuccess(NotFound(html.analyse.embed.notFound()))
|
||||
case _ => fuccess(NotFound(html.analyse.embed.notFound))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,13 +42,12 @@ object Auth extends LilaController {
|
|||
}
|
||||
|
||||
def authenticateUser(u: UserModel, result: Option[String => Result] = None)(implicit ctx: Context): Fu[Result] = {
|
||||
implicit val req = ctx.req
|
||||
if (u.ipBan) fuccess(Redirect(routes.Lobby.home))
|
||||
else api.saveAuthentication(u.id, ctx.mobileApiVersion) flatMap { sessionId =>
|
||||
negotiate(
|
||||
html = fuccess {
|
||||
val redirectTo = get("referrer").filter(goodReferrer) orElse
|
||||
req.session.get(api.AccessUri) getOrElse
|
||||
ctxReq.session.get(api.AccessUri) getOrElse
|
||||
routes.Lobby.home.url
|
||||
result.fold(Redirect(redirectTo))(_(redirectTo))
|
||||
},
|
||||
|
@ -118,8 +117,7 @@ object Auth extends LilaController {
|
|||
}
|
||||
|
||||
def logout = Open { implicit ctx =>
|
||||
implicit val req = ctx.req
|
||||
req.session get "sessionId" foreach lila.security.Store.delete
|
||||
ctxReq.session get "sessionId" foreach lila.security.Store.delete
|
||||
negotiate(
|
||||
html = Redirect(routes.Main.mobile).fuccess,
|
||||
api = _ => Ok(Json.obj("ok" -> true)).fuccess
|
||||
|
@ -128,11 +126,10 @@ object Auth extends LilaController {
|
|||
|
||||
// mobile app BC logout with GET
|
||||
def logoutGet = Open { implicit ctx =>
|
||||
implicit val req = ctx.req
|
||||
negotiate(
|
||||
html = notFound,
|
||||
api = _ => {
|
||||
req.session get "sessionId" foreach lila.security.Store.delete
|
||||
ctxReq.session get "sessionId" foreach lila.security.Store.delete
|
||||
Ok(Json.obj("ok" -> true)).withCookies(LilaCookie.newSession).fuccess
|
||||
}
|
||||
)
|
||||
|
@ -317,7 +314,6 @@ object Auth extends LilaController {
|
|||
}
|
||||
|
||||
private def redirectNewUser(user: UserModel)(implicit ctx: Context) = {
|
||||
implicit val req = ctx.req
|
||||
api.saveAuthentication(user.id, ctx.mobileApiVersion) flatMap { sessionId =>
|
||||
negotiate(
|
||||
html = Redirect(routes.User.show(user.username)).fuccess,
|
||||
|
|
|
@ -93,7 +93,6 @@ object Challenge extends LilaController {
|
|||
cond ?? {
|
||||
GameRepo.game(c.id).map {
|
||||
_ map { game =>
|
||||
implicit val req = ctx.req
|
||||
LilaCookie.cookie(
|
||||
AnonCookie.name,
|
||||
game.player(if (owner) c.finalColor else !c.finalColor).id,
|
||||
|
|
|
@ -47,7 +47,9 @@ private[controllers] trait LilaController
|
|||
api = _ => fuccess(jsonOkResult)
|
||||
)
|
||||
|
||||
implicit def lang(implicit ctx: Context) = ctx.lang
|
||||
implicit def ctxLang(implicit ctx: Context) = ctx.lang
|
||||
implicit def ctxReq(implicit ctx: Context) = ctx.req
|
||||
implicit def reqConfig(implicit req: RequestHeader) = ui.EmbedConfig(req)
|
||||
|
||||
protected def NoCache(res: Result): Result = res.withHeaders(
|
||||
CACHE_CONTROL -> "no-cache, no-store, must-revalidate", EXPIRES -> "0"
|
||||
|
@ -338,8 +340,7 @@ private[controllers] trait LilaController
|
|||
protected def authenticationFailed(implicit ctx: Context): Fu[Result] =
|
||||
negotiate(
|
||||
html = fuccess {
|
||||
implicit val req = ctx.req
|
||||
Redirect(routes.Auth.signup) withCookies LilaCookie.session(Env.security.api.AccessUri, req.uri)
|
||||
Redirect(routes.Auth.signup) withCookies LilaCookie.session(Env.security.api.AccessUri, ctx.req.uri)
|
||||
},
|
||||
api = _ => ensureSessionId(ctx.req) {
|
||||
Unauthorized(jsonError("Login required"))
|
||||
|
@ -362,8 +363,8 @@ private[controllers] trait LilaController
|
|||
if (req.session.data.contains(LilaCookie.sessionId)) res
|
||||
else res withCookies LilaCookie.makeSessionId(req)
|
||||
|
||||
protected def negotiate(html: => Fu[Result], api: ApiVersion => Fu[Result])(implicit ctx: Context): Fu[Result] =
|
||||
lila.api.Mobile.Api.requestVersion(ctx.req).fold(html) { v =>
|
||||
protected def negotiate(html: => Fu[Result], api: ApiVersion => Fu[Result])(implicit req: RequestHeader): Fu[Result] =
|
||||
lila.api.Mobile.Api.requestVersion(req).fold(html) { v =>
|
||||
api(v) dmap (_ as JSON)
|
||||
}.dmap(_.withHeaders("Vary" -> "Accept"))
|
||||
|
||||
|
|
|
@ -226,14 +226,9 @@ object Puzzle extends LilaController {
|
|||
}
|
||||
|
||||
def frame = Action.async { implicit req =>
|
||||
implicit val lang = lila.i18n.I18nLangPicker(req, none)
|
||||
env.daily.get map {
|
||||
case None => NotFound
|
||||
case Some(daily) => html.puzzle.embed(
|
||||
daily,
|
||||
get("bg", req) | "light",
|
||||
lila.pref.Theme(~get("theme", req)).cssClass
|
||||
)
|
||||
case Some(daily) => html.puzzle.embed(daily)(ui.EmbedConfig(req))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -218,7 +218,6 @@ object Setup extends LilaController with TheftPrevention {
|
|||
}
|
||||
|
||||
private[controllers] def redirectPov(pov: Pov)(implicit ctx: Context) = {
|
||||
implicit val req = ctx.req
|
||||
val redir = Redirect(routes.Round.watcher(pov.gameId, "white"))
|
||||
if (ctx.isAuth) redir
|
||||
else redir withCookies LilaCookie.cookie(
|
||||
|
|
|
@ -278,17 +278,17 @@ object Study extends LilaController {
|
|||
)
|
||||
}
|
||||
|
||||
def embed(id: String, chapterId: String) = Open { implicit ctx =>
|
||||
env.api.byIdWithChapter(id, chapterId) flatMap {
|
||||
def embed(id: String, chapterId: String) = Action.async { implicit req =>
|
||||
env.api.byIdWithChapter(id, chapterId).map(_.filterNot(_.study.isPrivate)) flatMap {
|
||||
_.fold(embedNotFound) {
|
||||
case WithChapter(study, chapter) => CanViewResult(study) {
|
||||
case WithChapter(study, chapter) =>
|
||||
env.jsonView(study.copy(
|
||||
members = lila.study.StudyMembers(Map.empty) // don't need no members
|
||||
), List(chapter.metadata), chapter, ctx.me) flatMap { studyJson =>
|
||||
), List(chapter.metadata), chapter, none) flatMap { studyJson =>
|
||||
val setup = chapter.setup
|
||||
val initialFen = chapter.root.fen.some
|
||||
val pov = UserAnalysis.makePov(initialFen, setup.variant)
|
||||
val baseData = Env.round.jsonView.userAnalysisJson(pov, ctx.pref, initialFen, setup.orientation, owner = false, me = ctx.me)
|
||||
val baseData = Env.round.jsonView.userAnalysisJson(pov, lila.pref.Pref.default, initialFen, setup.orientation, owner = false, me = none)
|
||||
val analysis = baseData ++ Json.obj(
|
||||
"treeParts" -> partitionTreeJsonWriter.writes {
|
||||
lila.study.TreeBuilder.makeRoot(chapter.root, setup.variant)
|
||||
|
@ -303,12 +303,11 @@ object Study extends LilaController {
|
|||
api = _ => Ok(Json.obj("study" -> data.study, "analysis" -> data.analysis)).fuccess
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} map NoCache
|
||||
}
|
||||
|
||||
private def embedNotFound(implicit ctx: Context): Fu[Result] =
|
||||
private def embedNotFound(implicit req: RequestHeader): Fu[Result] =
|
||||
fuccess(NotFound(html.study.embed.notFound()))
|
||||
|
||||
def cloneStudy(id: String) = Auth { implicit ctx => me =>
|
||||
|
|
|
@ -79,9 +79,8 @@ object Tv extends LilaController {
|
|||
/* for BC */
|
||||
def embed = Action { req =>
|
||||
Ok {
|
||||
val bg = get("bg", req) | "light"
|
||||
val theme = get("theme", req) | "brown"
|
||||
val url = s"""${req.domain + routes.Tv.frame}?bg=$bg&theme=$theme"""
|
||||
val config = ui.EmbedConfig(req)
|
||||
val url = s"""${req.domain + routes.Tv.frame}?bg=${config.bg}&theme=${config.board}"""
|
||||
s"""document.write("<iframe src='https://$url&embed=" + document.domain + "' class='lichess-tv-iframe' allowtransparency='true' frameBorder='0' style='width: 224px; height: 264px;' title='Lichess free online chess'></iframe>");"""
|
||||
} as JAVASCRIPT withHeaders (CACHE_CONTROL -> "max-age=86400")
|
||||
}
|
||||
|
@ -89,11 +88,7 @@ object Tv extends LilaController {
|
|||
def frame = Action.async { implicit req =>
|
||||
Env.tv.tv.getBestGame map {
|
||||
case None => NotFound
|
||||
case Some(game) => Ok(views.html.tv.embed(
|
||||
Pov first game,
|
||||
get("bg", req) | "light",
|
||||
lila.pref.Theme(~get("theme", req)).cssClass
|
||||
))
|
||||
case Some(game) => Ok(views.html.tv.embed(Pov first game, ui.EmbedConfig(req)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,12 +147,12 @@ object User extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def online = Open { implicit req =>
|
||||
def online = Action.async { implicit req =>
|
||||
val max = 50
|
||||
negotiate(
|
||||
html = notFound,
|
||||
html = notFoundJson(),
|
||||
api = _ => env.cached.getTop50Online map { list =>
|
||||
Ok(Json.toJson(list.take(getInt("nb").fold(10)(_ min max)).map(env.jsonView(_))))
|
||||
Ok(Json.toJson(list.take(getInt("nb", req).fold(10)(_ min max)).map(env.jsonView(_))))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import play.twirl.api.Html
|
|||
|
||||
import lila.api.Context
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.{ AssetVersion, ContentSecurityPolicy }
|
||||
import lila.common.{ Nonce, AssetVersion, ContentSecurityPolicy }
|
||||
|
||||
trait AssetHelper { self: I18nHelper with SecurityHelper =>
|
||||
|
||||
|
@ -135,4 +135,8 @@ trait AssetHelper { self: I18nHelper with SecurityHelper =>
|
|||
|
||||
def embedJs(js: Frag)(implicit ctx: Context): Frag = embedJsUnsafe(js.render)
|
||||
def embedJs(js: String)(implicit ctx: Context): Frag = embedJsUnsafe(js)
|
||||
|
||||
def embedJs(js: String, nonce: Nonce): Frag = raw {
|
||||
s"""<script nonce="$nonce">$js</script>"""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package lila.app
|
||||
package ui
|
||||
|
||||
import play.api.mvc.RequestHeader
|
||||
|
||||
import lila.common.{ Nonce, Lang }
|
||||
|
||||
case class EmbedConfig(bg: String, board: String, lang: Lang, req: RequestHeader, nonce: Nonce)
|
||||
|
||||
object EmbedConfig {
|
||||
|
||||
object implicits {
|
||||
implicit def configLang(implicit config: EmbedConfig): Lang = config.lang
|
||||
implicit def configReq(implicit config: EmbedConfig): RequestHeader = config.req
|
||||
}
|
||||
|
||||
def apply(req: RequestHeader): EmbedConfig = EmbedConfig(
|
||||
bg = get("bg", req) | "light",
|
||||
board = lila.pref.Theme(~get("theme", req)).cssClass,
|
||||
lang = lila.i18n.I18nLangPicker(req, none),
|
||||
req = req,
|
||||
nonce = Nonce.random
|
||||
)
|
||||
|
||||
private def get(name: String, req: RequestHeader): Option[String] =
|
||||
req.queryString get name flatMap (_.headOption) filter (_.nonEmpty)
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
package views.html.analyse
|
||||
|
||||
import play.api.libs.json.Json
|
||||
import play.api.libs.json.JsObject
|
||||
import play.api.mvc.RequestHeader
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.EmbedConfig
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.Lang
|
||||
import lila.common.String.html.safeJsonValue
|
||||
|
||||
import controllers.routes
|
||||
|
@ -12,22 +15,22 @@ import controllers.routes
|
|||
object embed {
|
||||
|
||||
import views.html.base.layout.bits._
|
||||
import EmbedConfig.implicits._
|
||||
|
||||
def apply(pov: lila.game.Pov, data: play.api.libs.json.JsObject)(implicit ctx: Context) = frag(
|
||||
def apply(pov: lila.game.Pov, data: JsObject)(implicit config: EmbedConfig) = frag(
|
||||
doctype,
|
||||
htmlTag(ctx)(
|
||||
htmlTag(config.lang)(
|
||||
topComment,
|
||||
head(
|
||||
charset,
|
||||
viewport,
|
||||
metaCsp(none),
|
||||
metaCsp(basicCsp withNonce config.nonce),
|
||||
st.headTitle(replay titleOf pov),
|
||||
pieceSprite(lila.pref.PieceSet.default),
|
||||
responsiveCssTag("analyse.round.embed")
|
||||
responsiveCssTagWithTheme("analyse.round.embed", config.bg)
|
||||
),
|
||||
body(cls := List(
|
||||
s"highlight ${ctx.currentBg} ${ctx.currentTheme.cssClass}" -> true,
|
||||
"piece-letter" -> ctx.pref.pieceNotationIsLetter
|
||||
s"highlight ${config.bg} ${config.board}" -> true
|
||||
))(
|
||||
div(cls := "is2d")(
|
||||
main(cls := "analyse")
|
||||
|
@ -53,23 +56,23 @@ object embed {
|
|||
data: ${safeJsonValue(data)},
|
||||
embed: true,
|
||||
i18n: ${views.html.board.userAnalysisI18n(withCeval = false, withExplorer = false)}
|
||||
});""")
|
||||
});""", config.nonce)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def notFound()(implicit ctx: Context) = frag(
|
||||
def notFound(implicit config: EmbedConfig) = frag(
|
||||
doctype,
|
||||
htmlTag(ctx)(
|
||||
htmlTag(config.lang)(
|
||||
topComment,
|
||||
head(
|
||||
charset,
|
||||
viewport,
|
||||
metaCsp(none),
|
||||
metaCsp(basicCsp),
|
||||
st.headTitle("404 - Game not found"),
|
||||
responsiveCssTag("analyse.round.embed")
|
||||
responsiveCssTagWithTheme("analyse.round.embed", "dark")
|
||||
),
|
||||
body(cls := ctx.currentBg)(
|
||||
body(cls := "dark")(
|
||||
div(cls := "not-found")(
|
||||
h1("Game not found")
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ import bits.dataPanel
|
|||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.Lang
|
||||
import lila.common.String.html.safeJsonValue
|
||||
import lila.game.Pov
|
||||
|
||||
|
@ -13,7 +14,7 @@ import controllers.routes
|
|||
|
||||
object replay {
|
||||
|
||||
private[analyse] def titleOf(pov: Pov)(implicit ctx: Context) =
|
||||
private[analyse] def titleOf(pov: Pov)(implicit lang: Lang) =
|
||||
s"${playerText(pov.game.whitePlayer)} vs ${playerText(pov.game.blackPlayer)}: ${pov.game.opening.fold(trans.analysis.txt())(_.opening.ecoName)}"
|
||||
|
||||
def apply(
|
||||
|
|
|
@ -3,7 +3,7 @@ package views.html.base
|
|||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.ContentSecurityPolicy
|
||||
import lila.common.{ Lang, ContentSecurityPolicy }
|
||||
|
||||
import controllers.routes
|
||||
|
||||
|
@ -11,7 +11,7 @@ object layout {
|
|||
|
||||
object bits {
|
||||
val doctype = raw("<!doctype html>")
|
||||
def htmlTag(implicit ctx: Context) = html(st.lang := ctx.lang.language)
|
||||
def htmlTag(implicit lang: Lang) = html(st.lang := lang.language)
|
||||
val topComment = raw("""<!-- Lichess is open source! See https://github.com/ornicar/lila -->""")
|
||||
val charset = raw("""<meta charset="utf-8">""")
|
||||
val viewport = raw("""<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/>""")
|
||||
|
@ -105,7 +105,7 @@ object layout {
|
|||
wrapClass: String = ""
|
||||
)(body: Frag)(implicit ctx: Context) = frag(
|
||||
doctype,
|
||||
htmlTag(ctx)(
|
||||
htmlTag(ctx.lang)(
|
||||
topComment,
|
||||
head(
|
||||
charset,
|
||||
|
|
|
@ -3,6 +3,7 @@ package views.html.board
|
|||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.common.String.html.safeJsonValue
|
||||
import lila.common.Lang
|
||||
import lila.i18n.{ I18nKeys => trans }
|
||||
|
||||
object userAnalysisI18n {
|
||||
|
@ -11,7 +12,7 @@ object userAnalysisI18n {
|
|||
withCeval: Boolean = true,
|
||||
withExplorer: Boolean = true,
|
||||
withForecast: Boolean = false
|
||||
)(implicit ctx: Context) = safeJsonValue(i18nJsObject(
|
||||
)(implicit lang: Lang) = safeJsonValue(i18nJsObject(
|
||||
baseTranslations ++ {
|
||||
withCeval ?? cevalTranslations
|
||||
} ++ {
|
||||
|
|
|
@ -5,27 +5,29 @@ import play.api.mvc.RequestHeader
|
|||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.Lang
|
||||
import lila.app.ui.EmbedConfig
|
||||
import views.html.base.layout.{ bits => layout }
|
||||
|
||||
import controllers.routes
|
||||
|
||||
object embed {
|
||||
|
||||
import EmbedConfig.implicits._
|
||||
|
||||
private val dataStreamUrl = attr("data-stream-url")
|
||||
|
||||
def apply(daily: lila.puzzle.DailyPuzzle, bg: String, board: String)(implicit req: RequestHeader, lang: Lang) = frag(
|
||||
def apply(daily: lila.puzzle.DailyPuzzle)(implicit config: EmbedConfig) = frag(
|
||||
layout.doctype,
|
||||
html(
|
||||
layout.htmlTag(config.lang)(
|
||||
head(
|
||||
layout.charset,
|
||||
layout.metaCsp(basicCsp),
|
||||
st.headTitle("lichess.org chess puzzle"),
|
||||
layout.pieceSprite(lila.pref.PieceSet.default),
|
||||
responsiveCssTagWithTheme("tv.embed", bg)
|
||||
responsiveCssTagWithTheme("tv.embed", config.bg)
|
||||
),
|
||||
body(
|
||||
cls := s"base $board merida",
|
||||
cls := s"base ${config.board}",
|
||||
dataStreamUrl := routes.Tv.feed
|
||||
)(
|
||||
div(id := "daily-puzzle", cls := "embedded", title := trans.clickToSolve.txt())(
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package views.html.study
|
||||
|
||||
import play.api.libs.json.Json
|
||||
import play.api.mvc.RequestHeader
|
||||
|
||||
import lila.api.Context
|
||||
import lila.app.templating.Environment._
|
||||
import lila.app.ui.ScalatagsTemplate._
|
||||
import lila.common.String.html.safeJsonValue
|
||||
import lila.common.Lang
|
||||
|
||||
import controllers.routes
|
||||
|
||||
|
@ -13,73 +15,74 @@ object embed {
|
|||
|
||||
import views.html.base.layout.bits._
|
||||
|
||||
private def bodyClass(implicit ctx: Context) = List(
|
||||
"base" -> true,
|
||||
ctx.currentTheme.cssClass -> true,
|
||||
(if (ctx.currentBg == "transp") "dark transp" else ctx.currentBg) -> true
|
||||
)
|
||||
// private def bodyClass(implicit ctx: Context) = List(
|
||||
// "base" -> true,
|
||||
// ctx.currentTheme.cssClass -> true,
|
||||
// (if (ctx.currentBg == "transp") "dark transp" else ctx.currentBg) -> true
|
||||
// )
|
||||
|
||||
def apply(s: lila.study.Study, chapter: lila.study.Chapter, data: lila.study.JsonView.JsData)(implicit ctx: Context) = frag(
|
||||
private implicit def lang(implicit req: RequestHeader): Lang = lila.i18n.I18nLangPicker(req, none)
|
||||
|
||||
def apply(s: lila.study.Study, chapter: lila.study.Chapter, data: lila.study.JsonView.JsData)(implicit req: RequestHeader) = frag(
|
||||
doctype,
|
||||
htmlTag(ctx)(
|
||||
topComment,
|
||||
head(
|
||||
charset,
|
||||
metaCsp(none),
|
||||
st.headTitle(s"${s.name} ${chapter.name}"),
|
||||
// fontStylesheets, // use proper stylesheets instead
|
||||
// currentBgCss,
|
||||
cssTags("common.css", "board.css", "analyse.css", "analyse-embed.css"),
|
||||
pieceSprite
|
||||
),
|
||||
body(cls := bodyClass ::: List(
|
||||
"highlight" -> true,
|
||||
"piece-letter" -> ctx.pref.pieceNotationIsLetter
|
||||
))(
|
||||
div(cls := "is2d")(
|
||||
div(cls := "embedded_study analyse cg-512")(miniBoardContent)
|
||||
),
|
||||
footer {
|
||||
val url = routes.Study.chapter(s.id.value, chapter.id.value)
|
||||
div(cls := "left")(
|
||||
a(target := "_blank", href := url)(h1(s.name.value)),
|
||||
" ",
|
||||
em("brought to you by ", a(target := "_blank", href := netBaseUrl)(netDomain))
|
||||
)
|
||||
a(target := "_blank", cls := "open", href := url)("Open")
|
||||
},
|
||||
jQueryTag,
|
||||
jsTag("vendor/mousetrap.js"),
|
||||
jsAt("compiled/util.js"),
|
||||
jsAt("compiled/trans.js"),
|
||||
analyseTag,
|
||||
jsTag("embed-analyse.js"),
|
||||
embedJs(s"""lichess.startEmbeddedAnalyse({
|
||||
element: document.querySelector('.embedded_study'),
|
||||
study: ${safeJsonValue(data.study)},
|
||||
data: ${safeJsonValue(data.analysis)},
|
||||
embed: true,
|
||||
i18n: ${views.html.board.userAnalysisI18n()},
|
||||
userId: null
|
||||
});""")
|
||||
)
|
||||
htmlTag(lang)( // topComment,
|
||||
// head(
|
||||
// charset,
|
||||
// metaCsp(basicCsp),
|
||||
// st.headTitle(s"${s.name} ${chapter.name}"),
|
||||
// // fontStylesheets, // use proper stylesheets instead
|
||||
// // currentBgCss,
|
||||
// cssTags("common.css", "board.css", "analyse.css", "analyse-embed.css"),
|
||||
// pieceSprite
|
||||
// ),
|
||||
// body(cls := bodyClass ::: List(
|
||||
// "highlight" -> true,
|
||||
// "piece-letter" -> ctx.pref.pieceNotationIsLetter
|
||||
// ))(
|
||||
// div(cls := "is2d")(
|
||||
// div(cls := "embedded_study analyse cg-512")(miniBoardContent)
|
||||
// ),
|
||||
// footer {
|
||||
// val url = routes.Study.chapter(s.id.value, chapter.id.value)
|
||||
// div(cls := "left")(
|
||||
// a(target := "_blank", href := url)(h1(s.name.value)),
|
||||
// " ",
|
||||
// em("brought to you by ", a(target := "_blank", href := netBaseUrl)(netDomain))
|
||||
// )
|
||||
// a(target := "_blank", cls := "open", href := url)("Open")
|
||||
// },
|
||||
// jQueryTag,
|
||||
// jsTag("vendor/mousetrap.js"),
|
||||
// jsAt("compiled/util.js"),
|
||||
// jsAt("compiled/trans.js"),
|
||||
// analyseTag,
|
||||
// jsTag("embed-analyse.js"),
|
||||
// embedJs(s"""lichess.startEmbeddedAnalyse({
|
||||
// element: document.querySelector('.embedded_study'),
|
||||
// study: ${safeJsonValue(data.study)},
|
||||
// data: ${safeJsonValue(data.analysis)},
|
||||
// embed: true,
|
||||
// i18n: ${views.html.board.userAnalysisI18n()},
|
||||
// userId: null
|
||||
// });""")
|
||||
// )
|
||||
)
|
||||
)
|
||||
|
||||
def notFound()(implicit ctx: Context) = frag(
|
||||
def notFound()(implicit req: RequestHeader) = frag(
|
||||
doctype,
|
||||
htmlTag(ctx)(
|
||||
htmlTag(lang)(
|
||||
topComment,
|
||||
head(
|
||||
charset,
|
||||
metaCsp(none),
|
||||
metaCsp(basicCsp),
|
||||
st.headTitle("404 - Study not available"),
|
||||
// fontStylesheets, // use proper stylesheets instead
|
||||
// currentBgCss,
|
||||
cssTags("common.css", "analyse-embed.css")
|
||||
),
|
||||
body(cls := bodyClass)(
|
||||
div(cls := "not_found")(
|
||||
body()(
|
||||
div(cls := "not-found")(
|
||||
h1("Study not available")
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,7 +13,7 @@ object embed {
|
|||
|
||||
private val dataStreamUrl = attr("data-stream-url")
|
||||
|
||||
def apply(pov: lila.game.Pov, bg: String, board: String)(implicit req: RequestHeader) = frag(
|
||||
def apply(pov: lila.game.Pov, config: lila.app.ui.EmbedConfig)(implicit req: RequestHeader) = frag(
|
||||
bits.doctype,
|
||||
html(
|
||||
head(
|
||||
|
@ -22,10 +22,10 @@ object embed {
|
|||
bits.metaCsp(basicCsp),
|
||||
st.headTitle("lichess.org chess TV"),
|
||||
bits.pieceSprite(lila.pref.PieceSet.default),
|
||||
responsiveCssTagWithTheme("tv.embed", bg)
|
||||
responsiveCssTagWithTheme("tv.embed", config.bg)
|
||||
),
|
||||
body(
|
||||
cls := s"base $board merida",
|
||||
cls := s"base ${config.board}",
|
||||
dataStreamUrl := routes.Tv.feed
|
||||
)(
|
||||
div(id := "featured-game", cls := "embedded", title := "lichess.org TV")(
|
||||
|
|
|
@ -90,6 +90,21 @@ private[api] final class RoundApi(
|
|||
}
|
||||
}.mon(_.round.api.watcher)
|
||||
|
||||
def embed(pov: Pov, apiVersion: ApiVersion,
|
||||
analysis: Option[Analysis] = None,
|
||||
initialFenO: Option[Option[FEN]] = None,
|
||||
withFlags: WithFlags): Fu[JsObject] =
|
||||
initialFenO.fold(GameRepo initialFen pov.game)(fuccess).flatMap { initialFen =>
|
||||
jsonView.watcherJson(pov, Pref.default, apiVersion, none, none,
|
||||
initialFen = initialFen,
|
||||
withFlags = withFlags) map { json =>
|
||||
(
|
||||
withTree(pov, analysis, initialFen, withFlags)_ compose
|
||||
withAnalysis(pov.game, analysis)_
|
||||
)(json)
|
||||
}
|
||||
}.mon(_.round.api.embed)
|
||||
|
||||
def userAnalysisJson(pov: Pov, pref: Pref, initialFen: Option[FEN], orientation: chess.Color, owner: Boolean, me: Option[User]) =
|
||||
owner.??(forecastApi loadForDisplay pov).map { fco =>
|
||||
withForecast(pov, owner, fco) {
|
||||
|
|
|
@ -171,6 +171,7 @@ object mon {
|
|||
object api {
|
||||
val player = rec("round.api.player")
|
||||
val watcher = rec("round.api.watcher")
|
||||
val embed = rec("round.api.embed")
|
||||
}
|
||||
object actor {
|
||||
val count = rec("round.actor.count")
|
||||
|
|
|
@ -134,4 +134,7 @@ lichess.startEmbeddedAnalyse = function(opts) {
|
|||
opts.initialPly = 'url';
|
||||
opts.trans = lichess.trans(opts.i18n);
|
||||
LichessAnalyse.start(opts);
|
||||
window.addEventListener('resize', function() {
|
||||
lichess.dispatchEvent(document.body, 'chessground.resize');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ function studyButton(ctrl: AnalyseCtrl) {
|
|||
'data-icon': '4'
|
||||
}
|
||||
}, ctrl.trans.noarg('openStudy'));
|
||||
if (ctrl.study || ctrl.ongoing) return;
|
||||
if (ctrl.study || ctrl.ongoing || ctrl.embed) return;
|
||||
return h('form', {
|
||||
attrs: {
|
||||
method: 'post',
|
||||
|
|
Loading…
Reference in New Issue