send ublog images to zulip, improve etiquette mention

ublog-markdown-preview
Thibault Duplessis 2021-09-05 11:09:32 +02:00
parent b8b122b86a
commit 79a3364b8a
15 changed files with 79 additions and 40 deletions

View File

@ -130,7 +130,7 @@ final class Ublog(env: Env) extends LilaController(env) {
ctx.body.body.file("image") match {
case Some(image) =>
ImageRateLimitPerIp(HTTPRequest ipAddress ctx.req) {
env.ublog.api.uploadImage(post, image) map { newPost =>
env.ublog.api.uploadImage(me, post, image) map { newPost =>
Ok(html.ublog.form.formImage(newPost))
} recover { case e: Exception =>
BadRequest(e.getMessage)

View File

@ -16,6 +16,8 @@ trait AssetHelper { self: I18nHelper with SecurityHelper =>
private lazy val minifiedAssets = env.net.minifiedAssets
lazy val vapidPublicKey = env.push.vapidPublicKey
lazy val picfitUrl = env.memo.picfitUrl
lazy val sameAssetDomain = netDomain.value == assetDomain.value
def assetVersion = AssetVersion.current
@ -26,8 +28,6 @@ trait AssetHelper { self: I18nHelper with SecurityHelper =>
def dbImageUrl(path: String) = s"$assetBaseUrl/image/$path"
lazy val picfitUrl = new lila.memo.PicfitUrl(env.net.picfitEndpoint, env.net.picfitSecretKey)
def cssTag(name: String)(implicit ctx: Context): Frag =
cssTagWithTheme(name, ctx.currentBg)

View File

@ -21,6 +21,7 @@ object form {
) {
main(cls := "box box-pad page page-small ublog-post-form")(
h1(trans.ublog.newPost()),
etiquette,
inner(user, f, none)
)
}
@ -90,18 +91,18 @@ object form {
)(form3.textarea(_)(rows := 30)),
form3.actions(
a(href := post.fold(routes.Ublog.index(user.username))(views.html.ublog.post.urlOf))(trans.cancel()),
span(
etiquette,
form3.submit(trans.apply())
)
form3.submit(trans.apply())
)
)
private def etiquette(implicit ctx: Context) =
private def etiquette(implicit ctx: Context) = div(cls := "ublog-post-form__etiquette")(
p("Please only post safe and respectful content."),
p("Anything even slightly inappropriate could get your account closed."),
a(
dataIcon := "",
href := routes.Page.loneBookmark("blog-etiquette"),
cls := "text ublog-post-form__etiquette",
cls := "text",
targetBlank
)("Blog Etiquette")
)
}

View File

@ -89,21 +89,13 @@ object post {
def editUrlOf(post: UblogPost.BasePost) = routes.Ublog.edit(usernameOrId(post.user), post.id.value)
object thumbnail {
sealed abstract class Size(val width: Int) {
def height = width * 10 / 16
}
case object Large extends Size(880)
case object Small extends Size(400)
def apply(post: UblogPost.BasePost, size: thumbnail.type => Size) =
def apply(post: UblogPost.BasePost, size: UblogPost.thumbnail.SizeSelector) =
img(cls := "ublog-post-image")(src := url(post, size))
def url(post: UblogPost.BasePost, size: thumbnail.type => Size) = {
val s = size(thumbnail)
def url(post: UblogPost.BasePost, size: UblogPost.thumbnail.SizeSelector) =
post.image match {
case Some(image) => picfitUrl.thumbnail(image, s.width, s.height)
case Some(image) => UblogPost.thumbnail(picfitUrl, image, size)
case _ => assetUrl("images/user-blog-default.png")
}
}
}
}

View File

@ -113,7 +113,7 @@ lazy val blog = module("blog",
)
lazy val ublog = module("ublog",
Seq(common, memo, timeline),
Seq(common, memo, timeline, irc),
Seq(specs2) ++ reactivemongo.bundle
)

View File

@ -17,8 +17,6 @@ net {
prodDomain = "lichess.org"
http.log = true
stage.banner = false
picfitEndpoint = ${memo.picfit.endpointGet}
picfitSecretKey = ${memo.picfit.secretKey}
}
play {
application.loader = "lila.app.AppLoader"

View File

@ -42,9 +42,7 @@ object config {
@ConfigName("socket.domains") socketDomains: List[String],
crawlable: Boolean,
@ConfigName("ratelimit") rateLimit: RateLimit,
email: EmailAddress,
picfitEndpoint: String,
picfitSecretKey: Secret
email: EmailAddress
) {
def isProd = domain == prodDomain
}

View File

@ -96,6 +96,18 @@ final class IrcApi(
def broadcastError(id: String, name: String, error: String): Funit =
zulip(_.broadcast, "lila error log")(s"${markdown.broadcastLink(id, name)} $error")
def ublogImage(
user: User,
id: String,
slug: String,
title: String,
filename: String,
imageUrl: String
): Funit =
zulip(_.image, "blog")(
s"![$filename]($imageUrl) [$title](/@/${user.username}/blog/$slug/$id) by ${markdown.userLink(user)}"
)
def userAppeal(user: User, mod: Holder): Funit =
zulip
.sendAndGetLink(_.mod.adminAppeal, "/" + user.username)(

View File

@ -91,6 +91,7 @@ private object ZulipClient {
}
val general = "general"
val broadcast = "content-broadcast"
val image = "mod-comms-image"
type Selector = ZulipClient.stream.type => String
}
}

View File

@ -41,4 +41,6 @@ final class Env(
lazy val mongoRateLimitApi = wire[MongoRateLimitApi]
lazy val picfitApi = new PicfitApi(db(config.picfit.collection), ws, config.picfit)
lazy val picfitUrl = new lila.memo.PicfitUrl(config.picfit)
}

View File

@ -128,7 +128,7 @@ object PicfitApi {
}
}
final class PicfitUrl(endpoint: String, secretKey: config.Secret) {
final class PicfitUrl(config: PicfitConfig) {
// This operation will able you to resize the image to the specified width and height.
// Preserves the aspect ratio
@ -155,11 +155,11 @@ final class PicfitUrl(endpoint: String, secretKey: config.Secret) {
) = {
// parameters must be given in alphabetical order for the signature to work (!)
val queryString = s"h=$height&op=$operation&path=$id&w=$width"
s"$endpoint/display?${signQueryString(queryString)}"
s"${config.endpointGet}/display?${signQueryString(queryString)}"
}
private object signQueryString {
private val signer = com.roundeights.hasher.Algo hmac secretKey.value
private val signer = com.roundeights.hasher.Algo hmac config.secretKey.value
private val cache: LoadingCache[String, String] =
CacheApi.scaffeineNoScheduler
.expireAfterWrite(10 minutes)

View File

@ -9,7 +9,9 @@ final class Env(
db: lila.db.Db,
userRepo: lila.user.UserRepo,
timeline: lila.hub.actors.Timeline,
picfit: lila.memo.PicfitApi
picfitApi: lila.memo.PicfitApi,
picfitUrl: lila.memo.PicfitUrl,
ircApi: lila.irc.IrcApi
)(implicit
ec: scala.concurrent.ExecutionContext
) {

View File

@ -9,11 +9,17 @@ import lila.common.config.MaxPerPage
import lila.common.paginator.Paginator
import lila.db.dsl._
import lila.db.paginator.Adapter
import lila.memo.PicfitApi
import lila.memo.{ PicfitApi, PicfitUrl }
import lila.user.User
import lila.hub.actorApi.timeline.Propagate
final class UblogApi(coll: Coll, picfitApi: PicfitApi, timeline: lila.hub.actors.Timeline)(implicit
final class UblogApi(
coll: Coll,
picfitApi: PicfitApi,
picfitUrl: PicfitUrl,
timeline: lila.hub.actors.Timeline,
irc: lila.irc.IrcApi
)(implicit
ec: ExecutionContext
) {
@ -61,13 +67,21 @@ final class UblogApi(coll: Coll, picfitApi: PicfitApi, timeline: lila.hub.actors
private def imageRel(post: UblogPost) = s"ublog:${post.id}"
def uploadImage(post: UblogPost, picture: PicfitApi.Uploaded): Fu[UblogPost] =
picfitApi
.upload(imageRel(post), picture, userId = post.user)
.flatMap { image =>
coll.update.one($id(post.id), $set("image" -> image.id)) inject post.copy(image = image.id.some)
}
.logFailure(logger branch "upload")
def uploadImage(user: User, post: UblogPost, picture: PicfitApi.Uploaded): Fu[UblogPost] = {
for {
image <- picfitApi
.upload(imageRel(post), picture, userId = post.user)
_ <- coll.update.one($id(post.id), $set("image" -> image.id))
_ <- post.live ?? irc.ublogImage(
user,
id = post.id.value,
slug = post.slug,
title = post.title,
filename = image.name,
imageUrl = UblogPost.thumbnail(picfitUrl, image.id, _.Small)
)
} yield post.copy(image = image.id.some)
}.logFailure(logger branch "upload")
def lightsByIds(ids: List[UblogPost.Id]): Fu[List[UblogPost.LightPost]] =
coll

View File

@ -2,7 +2,7 @@ package lila.ublog
import org.joda.time.DateTime
import lila.memo.PicfitImage
import lila.memo.{ PicfitImage, PicfitUrl }
import lila.user.User
case class UblogPost(
@ -56,4 +56,16 @@ object UblogPost {
val s = lila.common.String slugify title
if (s.isEmpty) "-" else s
}
object thumbnail {
sealed abstract class Size(val width: Int) {
def height = width * 10 / 16
}
case object Large extends Size(880)
case object Small extends Size(400)
type SizeSelector = thumbnail.type => Size
def apply(picfitUrl: PicfitUrl, image: PicfitImage.Id, size: SizeSelector) =
picfitUrl.thumbnail(image, size(thumbnail).width, size(thumbnail).height)
}
}

View File

@ -16,4 +16,11 @@
align-items: center;
}
}
&__etiquette {
@extend %box-neat;
background: $c-bg-zebra;
padding: 2em 3em;
margin: 3em auto;
max-width: 80ch;
}
}