ublog post image alt & credit
parent
154382ca19
commit
503beddb89
|
@ -26,7 +26,7 @@ object form {
|
|||
) {
|
||||
main(cls := "page-menu page-small")(
|
||||
views.html.blog.bits.menu(none, "mine".some),
|
||||
div(cls := "page-menu__content box box-pad ublog-post-form")(
|
||||
div(cls := "page-menu__content box ublog-post-form")(
|
||||
standardFlash(),
|
||||
h1(trans.ublog.newPost()),
|
||||
etiquette,
|
||||
|
@ -43,7 +43,7 @@ object form {
|
|||
) {
|
||||
main(cls := "page-menu page-small")(
|
||||
views.html.blog.bits.menu(none, "mine".some),
|
||||
div(cls := "page-menu__content box box-pad ublog-post-form")(
|
||||
div(cls := "page-menu__content box ublog-post-form")(
|
||||
standardFlash(),
|
||||
div(cls := "box__top")(
|
||||
h1(
|
||||
|
@ -108,32 +108,41 @@ object form {
|
|||
)
|
||||
)
|
||||
|
||||
def formImage(post: UblogPost) = postView.thumbnail(post, _.Small)
|
||||
def formImage(post: UblogPost) =
|
||||
postView.thumbnail(post, _.Small)(cls := post.image.isDefined.option("user-image"))
|
||||
|
||||
private def inner(form: Form[UblogPostData], post: Either[User, UblogPost], captcha: Option[Captcha])(
|
||||
implicit ctx: Context
|
||||
) =
|
||||
postForm(
|
||||
cls := "form3",
|
||||
cls := "form3 ublog-post-form__main",
|
||||
action := post.fold(_ => routes.Ublog.create, p => routes.Ublog.update(p.id.value))
|
||||
)(
|
||||
form3.globalError(form),
|
||||
post.isRight option form3.split(
|
||||
form3.checkbox(
|
||||
form("live"),
|
||||
trans.ublog.publishOnYourBlog(),
|
||||
help = trans.ublog.publishHelp().some,
|
||||
half = true
|
||||
),
|
||||
form3.group(form("language"), trans.language(), half = true) { field =>
|
||||
form3.select(
|
||||
field,
|
||||
LangList.popularNoRegion.map { l =>
|
||||
l.code -> l.toLocale.getDisplayLanguage
|
||||
post.toOption.map { p =>
|
||||
frag(
|
||||
form3.split(
|
||||
form3.group(form("imageAlt"), trans.ublog.imageAlt(), half = true)(form3.input(_)),
|
||||
form3.group(form("imageCredit"), trans.ublog.imageCredit(), half = true)(form3.input(_))
|
||||
)(cls := s"ublog-post-form__image-text ${p.image.isDefined ?? "visible"}"),
|
||||
form3.split(
|
||||
form3.checkbox(
|
||||
form("live"),
|
||||
trans.ublog.publishOnYourBlog(),
|
||||
help = trans.ublog.publishHelp().some,
|
||||
half = true
|
||||
),
|
||||
form3.group(form("language"), trans.language(), half = true) { field =>
|
||||
form3.select(
|
||||
field,
|
||||
LangList.popularNoRegion.map { l =>
|
||||
l.code -> l.toLocale.getDisplayLanguage
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
},
|
||||
form3.group(form("title"), trans.ublog.postTitle())(form3.input(_)(autofocus)),
|
||||
form3.group(form("intro"), trans.ublog.postIntro())(form3.input(_)(autofocus)),
|
||||
form3.group(
|
||||
|
|
|
@ -44,7 +44,12 @@ object post {
|
|||
main(cls := "page-menu page-small")(
|
||||
views.html.blog.bits.menu(none, (if (ctx is user) "mine" else "community").some),
|
||||
div(cls := "page-menu__content box box-pad ublog-post")(
|
||||
post.image.isDefined option thumbnail(post, _.Large)(cls := "ublog-post__image"),
|
||||
post.image.map { image =>
|
||||
frag(
|
||||
thumbnail(post, _.Large)(cls := "ublog-post__image"),
|
||||
image.credit.map { p(cls := "ublog-post__image-credit")(_) }
|
||||
)
|
||||
},
|
||||
ctx.is(user) || isGranted(_.ModerateBlog) option standardFlash(),
|
||||
h1(cls := "ublog-post__title")(post.title),
|
||||
div(cls := "ublog-post__meta")(
|
||||
|
@ -191,12 +196,13 @@ object post {
|
|||
img(
|
||||
cls := "ublog-post-image",
|
||||
widthA := size(UblogPost.thumbnail).width,
|
||||
heightA := size(UblogPost.thumbnail).height
|
||||
heightA := size(UblogPost.thumbnail).height,
|
||||
alt := post.image.flatMap(_.alt)
|
||||
)(src := url(post, size))
|
||||
|
||||
def url(post: UblogPost.BasePost, size: UblogPost.thumbnail.SizeSelector) =
|
||||
post.image match {
|
||||
case Some(image) => UblogPost.thumbnail(picfitUrl, image, size)
|
||||
case Some(image) => UblogPost.thumbnail(picfitUrl, image.id, size)
|
||||
case _ => assetUrl("images/user-blog-default.png")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
db.ublog_post
|
||||
.find({ image: { $exists: 1 } }, { image: 1 })
|
||||
.forEach(p => db.ublog_post.update({ _id: p._id }, { $set: { image: { id: p.image } } }));
|
|
@ -161,7 +161,7 @@ final class PersonalDataExport(
|
|||
"title" -> post.title,
|
||||
"intro" -> post.intro,
|
||||
"body" -> post.markdown,
|
||||
"image" -> post.image.??(lila.ublog.UblogPost.thumbnail(picfitUrl, _, _.Large)),
|
||||
"image" -> post.image.??(i => lila.ublog.UblogPost.thumbnail(picfitUrl, i.id, _.Large)),
|
||||
"topics" -> post.topics.map(_.value).mkString(", ")
|
||||
).map { case (k, v) =>
|
||||
s"$k: $v"
|
||||
|
|
|
@ -2283,6 +2283,8 @@ val `noPostsInThisBlogYet` = new I18nKey("ublog:noPostsInThisBlogYet")
|
|||
val `noDrafts` = new I18nKey("ublog:noDrafts")
|
||||
val `latestBlogPosts` = new I18nKey("ublog:latestBlogPosts")
|
||||
val `uploadAnImageForYourPost` = new I18nKey("ublog:uploadAnImageForYourPost")
|
||||
val `imageAlt` = new I18nKey("ublog:imageAlt")
|
||||
val `imageCredit` = new I18nKey("ublog:imageCredit")
|
||||
val `publishedNbBlogPosts` = new I18nKey("ublog:publishedNbBlogPosts")
|
||||
val `nbViews` = new I18nKey("ublog:nbViews")
|
||||
val `viewAllNbPosts` = new I18nKey("ublog:viewAllNbPosts")
|
||||
|
|
|
@ -110,10 +110,10 @@ final class UblogApi(
|
|||
|
||||
def uploadImage(user: User, post: UblogPost, picture: PicfitApi.FilePart): Fu[UblogPost] =
|
||||
for {
|
||||
image <- picfitApi
|
||||
.uploadFile(imageRel(post), picture, userId = user.id)
|
||||
_ <- colls.post.update.one($id(post.id), $set("image" -> image.id))
|
||||
} yield post.copy(image = image.id.some)
|
||||
pic <- picfitApi.uploadFile(imageRel(post), picture, userId = user.id)
|
||||
image = post.image.fold(UblogImage(pic.id))(_.copy(id = pic.id))
|
||||
_ <- colls.post.updateField($id(post.id), "image", image)
|
||||
} yield post.copy(image = image.some)
|
||||
|
||||
def deleteImage(post: UblogPost): Fu[UblogPost] =
|
||||
picfitApi.deleteByRel(imageRel(post)) >>
|
||||
|
|
|
@ -25,6 +25,7 @@ private object UblogBsonHandlers {
|
|||
.afterRead(_.filter(t => UblogTopic.exists(t.value)))
|
||||
implicit val langBsonHandler = stringAnyValHandler[Lang](_.code, Lang.apply)
|
||||
implicit val recordedBSONHandler = Macros.handler[Recorded]
|
||||
implicit val imageBSONHandler = Macros.handler[UblogImage]
|
||||
implicit val likesBSONHandler = intAnyValHandler[Likes](_.value, Likes)
|
||||
implicit val viewsBSONHandler = intAnyValHandler[Views](_.value, Views)
|
||||
implicit val rankBSONHandler = dateIsoHandler[Rank](Iso[DateTime, Rank](Rank, _.value))
|
||||
|
|
|
@ -17,14 +17,16 @@ final class UblogForm(markup: UblogMarkup, val captcher: lila.hub.actors.Captche
|
|||
|
||||
private val base =
|
||||
mapping(
|
||||
"title" -> cleanNonEmptyText(minLength = 3, maxLength = 80),
|
||||
"intro" -> cleanNonEmptyText(minLength = 0, maxLength = 1_000),
|
||||
"markdown" -> cleanNonEmptyText(minLength = 0, maxLength = 100_000).verifying(markdownImage.constraint),
|
||||
"language" -> optional(stringIn(LangList.popularNoRegion.map(_.code).toSet)),
|
||||
"topics" -> optional(text),
|
||||
"live" -> boolean,
|
||||
"gameId" -> text,
|
||||
"move" -> text
|
||||
"title" -> cleanNonEmptyText(minLength = 3, maxLength = 80),
|
||||
"intro" -> cleanNonEmptyText(minLength = 0, maxLength = 1_000),
|
||||
"markdown" -> cleanNonEmptyText(minLength = 0, maxLength = 100_000).verifying(markdownImage.constraint),
|
||||
"imageAlt" -> optional(cleanNonEmptyText(minLength = 3, maxLength = 200)),
|
||||
"imageCredit" -> optional(cleanNonEmptyText(minLength = 3, maxLength = 200)),
|
||||
"language" -> optional(stringIn(LangList.popularNoRegion.map(_.code).toSet)),
|
||||
"topics" -> optional(text),
|
||||
"live" -> boolean,
|
||||
"gameId" -> text,
|
||||
"move" -> text
|
||||
)(UblogPostData.apply)(UblogPostData.unapply)
|
||||
|
||||
val create = Form(
|
||||
|
@ -37,6 +39,8 @@ final class UblogForm(markup: UblogMarkup, val captcher: lila.hub.actors.Captche
|
|||
title = post.title,
|
||||
intro = post.intro,
|
||||
markdown = post.markdown,
|
||||
imageAlt = post.image.flatMap(_.alt),
|
||||
imageCredit = post.image.flatMap(_.credit),
|
||||
language = post.language.code.some,
|
||||
topics = post.topics.map(_.value).mkString(", ").some,
|
||||
live = post.live,
|
||||
|
@ -52,6 +56,8 @@ object UblogForm {
|
|||
title: String,
|
||||
intro: String,
|
||||
markdown: String,
|
||||
imageAlt: Option[String],
|
||||
imageCredit: Option[String],
|
||||
language: Option[String],
|
||||
topics: Option[String],
|
||||
live: Boolean,
|
||||
|
@ -84,6 +90,9 @@ object UblogForm {
|
|||
title = title,
|
||||
intro = intro,
|
||||
markdown = markdown,
|
||||
image = prev.image.map { i =>
|
||||
i.copy(alt = imageAlt, credit = imageCredit)
|
||||
},
|
||||
language = LangList.removeRegion(realLanguage | prev.language),
|
||||
topics = topics ?? UblogTopic.fromStrList,
|
||||
live = live,
|
||||
|
|
|
@ -13,7 +13,7 @@ case class UblogPost(
|
|||
intro: String,
|
||||
markdown: String,
|
||||
language: Lang,
|
||||
image: Option[PicfitImage.Id],
|
||||
image: Option[UblogImage],
|
||||
topics: List[UblogTopic],
|
||||
live: Boolean,
|
||||
created: UblogPost.Recorded,
|
||||
|
@ -28,6 +28,8 @@ case class UblogPost(
|
|||
def indexable = live && topics.exists(t => UblogTopic.chessExists(t.value))
|
||||
}
|
||||
|
||||
case class UblogImage(id: PicfitImage.Id, alt: Option[String] = None, credit: Option[String] = None)
|
||||
|
||||
object UblogPost {
|
||||
|
||||
case class Id(value: String) extends AnyVal with StringValue
|
||||
|
@ -50,7 +52,7 @@ object UblogPost {
|
|||
val blog: UblogBlog.Id
|
||||
val title: String
|
||||
val intro: String
|
||||
val image: Option[PicfitImage.Id]
|
||||
val image: Option[UblogImage]
|
||||
val created: Recorded
|
||||
val lived: Option[Recorded]
|
||||
def id = _id
|
||||
|
@ -62,7 +64,7 @@ object UblogPost {
|
|||
blog: UblogBlog.Id,
|
||||
title: String,
|
||||
intro: String,
|
||||
image: Option[PicfitImage.Id],
|
||||
image: Option[UblogImage],
|
||||
created: Recorded,
|
||||
lived: Option[Recorded]
|
||||
) extends BasePost
|
||||
|
|
|
@ -31,4 +31,6 @@
|
|||
<item quantity="other">View all %s posts</item>
|
||||
</plurals>
|
||||
<string name="uploadAnImageForYourPost">Upload an image for your post</string>
|
||||
<string name="imageAlt">Image alternative text</string>
|
||||
<string name="imageCredit">Image credit</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,35 @@
|
|||
@import 'flash';
|
||||
|
||||
.ublog-post-form {
|
||||
padding-bottom: 5vh;
|
||||
|
||||
&__main .form-split,
|
||||
&__main > .form-group,
|
||||
&__main .form-actions,
|
||||
&__delete .form-actions,
|
||||
&__image {
|
||||
padding-left: var(--box-padding);
|
||||
padding-right: var(--box-padding);
|
||||
}
|
||||
&__image {
|
||||
background: $c-bg-zebra;
|
||||
padding-top: 5vh;
|
||||
}
|
||||
&__image-text {
|
||||
background: $c-bg-zebra;
|
||||
margin-bottom: 5vh;
|
||||
border-bottom: $border;
|
||||
font-size: 0.9em;
|
||||
.form-group {
|
||||
display: none;
|
||||
}
|
||||
&.visible {
|
||||
height: auto;
|
||||
.form-group {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ublog-post-image {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
@ -13,10 +42,8 @@
|
|||
}
|
||||
&__image {
|
||||
.form-group {
|
||||
text-align: center;
|
||||
@extend %flex-column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
@extend %box-neat;
|
||||
|
|
|
@ -84,6 +84,11 @@
|
|||
@extend %box-neat-force;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
&-credit {
|
||||
font-size: 0.9em;
|
||||
color: $c-font-dim;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
&__intro {
|
||||
@extend %break-word;
|
||||
|
|
|
@ -28,11 +28,16 @@ const setupTopics = (el: HTMLTextAreaElement) =>
|
|||
});
|
||||
|
||||
const setupImage = (form: HTMLFormElement) => {
|
||||
const showText = () =>
|
||||
$('.ublog-post-form__image-text').toggleClass('visible', $('.ublog-post-image').hasClass('user-image'));
|
||||
const submit = () => {
|
||||
const replace = (html: string) => $(form).find('.ublog-post-image').replaceWith(html);
|
||||
const wrap = (html: string) => '<div class="ublog-post-image">' + html + '</div>';
|
||||
xhr.formToXhr(form).then(
|
||||
html => replace(html),
|
||||
html => {
|
||||
replace(html);
|
||||
showText();
|
||||
},
|
||||
err => replace(wrap(`<bad>${err}</bad>`))
|
||||
);
|
||||
replace(wrap(spinner));
|
||||
|
@ -40,6 +45,7 @@ const setupImage = (form: HTMLFormElement) => {
|
|||
};
|
||||
$(form).on('submit', submit);
|
||||
$(form).find('input[name="image"]').on('change', submit);
|
||||
showText();
|
||||
};
|
||||
|
||||
const setupMarkdownEditor = (el: HTMLTextAreaElement) => {
|
||||
|
|
Loading…
Reference in New Issue