Browse Source

Slightly improve the media experience (#452)

* Slightly improve the media experience

- Use a grid to display the list of media
- Add icons for non-image media preview
- Paginate the gallery
- Add links to the gallery in the editor and in the profile settings to make it more discoverable when you need it

Fixes #432

* Allow video and audio tags in SafeString

Otherwise we can't display their preview, nor show them in articles

Also show controls by default for these two elements

* Show fallback images for audio and unknown files, to make them more visible

* Add a new constructor to SafeString when the input is trusted and doesn't need to be escaped.

And use it to generate media previews.

* Make it possible to insert video/audio in articles
pull/471/head
Baptiste Gelez 3 years ago
committed by GitHub
parent
commit
eff2698664
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .editorconfig
  2. 62
      plume-models/src/medias.rs
  3. 24
      plume-models/src/safe_string.rs
  4. 24
      po/plume/ar.po
  5. 23
      po/plume/de.po
  6. 20
      po/plume/en.po
  7. 20
      po/plume/es.po
  8. 23
      po/plume/fr.po
  9. 23
      po/plume/gl.po
  10. 23
      po/plume/it.po
  11. 24
      po/plume/ja.po
  12. 22
      po/plume/nb.po
  13. 24
      po/plume/pl.po
  14. 18
      po/plume/plume.pot
  15. 24
      po/plume/pt.po
  16. 24
      po/plume/ru.po
  17. 17
      src/routes/medias.rs
  18. 28
      static/css/_global.scss
  19. 65
      static/images/audio-file.svg
  20. 65
      static/images/unknown-file.svg
  21. 115
      static/images/video-file.svg
  22. 2
      templates/medias/details.rs.html
  23. 50
      templates/medias/index.rs.html
  24. 4
      templates/partials/post_card.rs.html
  25. 4
      templates/posts/new.rs.html
  26. 2
      templates/users/dashboard.rs.html
  27. 4
      templates/users/edit.rs.html

1
.editorconfig

@ -7,6 +7,7 @@ trim_trailing_whitespace = true
[*.{js,rs,css,tera,html}]
charset = utf-8
indent_size = 4
[*.{rs,tera,css,html}]
indent_style = space

62
plume-models/src/medias.rs

@ -45,6 +45,17 @@ pub enum MediaCategory {
Unknown,
}
impl MediaCategory {
pub fn to_string(&self) -> &str {
match *self {
MediaCategory::Image => "image",
MediaCategory::Audio => "audio",
MediaCategory::Video => "video",
MediaCategory::Unknown => "unknown",
}
}
}
impl Media {
insert!(medias, NewMedia);
get!(medias);
@ -56,6 +67,23 @@ impl Media {
.map_err(Error::from)
}
pub fn page_for_user(conn: &Connection, user: &User, (min, max): (i32, i32)) -> Result<Vec<Media>> {
medias::table
.filter(medias::owner_id.eq(user.id))
.offset(min as i64)
.limit((max - min) as i64)
.load::<Media>(conn)
.map_err(Error::from)
}
pub fn count_for_user(conn: &Connection, user: &User) -> Result<i64> {
medias::table
.filter(medias::owner_id.eq(user.id))
.count()
.get_result(conn)
.map_err(Error::from)
}
pub fn category(&self) -> MediaCategory {
match &*self
.file_path
@ -71,41 +99,25 @@ impl Media {
}
}
pub fn preview_html(&self, conn: &Connection) -> Result<SafeString> {
let url = self.url(conn)?;
Ok(match self.category() {
MediaCategory::Image => SafeString::new(&format!(
r#"<img src="{}" alt="{}" title="{}" class=\"preview\">"#,
url, escape(&self.alt_text), escape(&self.alt_text)
)),
MediaCategory::Audio => SafeString::new(&format!(
r#"<audio src="{}" title="{}" class="preview"></audio>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Video => SafeString::new(&format!(
r#"<video src="{}" title="{}" class="preview"></video>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Unknown => SafeString::new(""),
})
}
pub fn html(&self, conn: &Connection) -> Result<SafeString> {
let url = self.url(conn)?;
Ok(match self.category() {
MediaCategory::Image => SafeString::new(&format!(
MediaCategory::Image => SafeString::trusted(&format!(
r#"<img src="{}" alt="{}" title="{}">"#,
url, escape(&self.alt_text), escape(&self.alt_text)
)),
MediaCategory::Audio => SafeString::new(&format!(
r#"<audio src="{}" title="{}"></audio>"#,
MediaCategory::Audio => SafeString::trusted(&format!(
r#"<div class="media-preview audio"></div><audio src="{}" title="{}" controls></audio>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Video => SafeString::new(&format!(
r#"<video src="{}" title="{}"></video>"#,
MediaCategory::Video => SafeString::trusted(&format!(
r#"<video src="{}" title="{}" controls></video>"#,
url, escape(&self.alt_text)
)),
MediaCategory::Unknown => SafeString::new(""),
MediaCategory::Unknown => SafeString::trusted(&format!(
r#"<a href="{}" class="media-preview unknown"></a>"#,
url,
)),
})
}

24
plume-models/src/safe_string.rs

@ -19,12 +19,20 @@ lazy_static! {
static ref CLEAN: Builder<'static> = {
let mut b = Builder::new();
b.add_generic_attributes(iter::once("id"))
.add_tags(iter::once("iframe"))
.add_tags(&[ "iframe", "video", "audio" ])
.id_prefix(Some("postcontent-"))
.url_relative(UrlRelative::Custom(Box::new(url_add_prefix)))
.add_tag_attributes(
"iframe",
["width", "height", "src", "frameborder"].iter().map(|&v| v),
[ "width", "height", "src", "frameborder" ].iter().map(|&v| v),
)
.add_tag_attributes(
"video",
[ "src", "title", "controls" ].iter(),
)
.add_tag_attributes(
"audio",
[ "src", "title", "controls" ].iter(),
);
b
};
@ -53,6 +61,18 @@ impl SafeString {
value: CLEAN.clean(&value).to_string(),
}
}
/// Creates a new `SafeString`, but without escaping the given value.
///
/// Only use when you are sure you can trust the input (when the HTML
/// is entirely generated by Plume, not depending on user-inputed data).
/// Prefer `SafeString::new` as much as possible.
pub fn trusted(value: impl AsRef<str>) -> Self {
SafeString {
value: value.as_ref().to_string()
}
}
pub fn set(&mut self, value: &str) {
self.value = CLEAN.clean(value).to_string();
}

24
po/plume/ar.po

@ -236,6 +236,13 @@ msgstr "تعديل حسابك"
msgid "Your Profile"
msgstr "ملفك الشخصي"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "استخدمها كصورة رمزية"
#, fuzzy
msgid "Display name"
msgstr "الاسم العلني"
@ -526,6 +533,15 @@ msgstr "العنوان الثانوي"
msgid "Content"
msgstr "المحتوى"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "إرسال"
# src/template_utils.rs:144
msgid "Tags, separated by commas"
msgstr ""
@ -719,9 +735,17 @@ msgstr "إرسال"
msgid "You don't have any media yet."
msgstr "ليس لديك أية وسائط بعد."
#, fuzzy
msgid "Content warning: {0}"
msgstr "تحذير عن المحتوى"
msgid "Delete"
msgstr "حذف"
#, fuzzy
msgid "Details"
msgstr "تفاصيل الصورة"
msgid "Media upload"
msgstr "إرسال الوسائط"

23
po/plume/de.po

@ -248,6 +248,13 @@ msgstr "Ändere deinen Account"
msgid "Your Profile"
msgstr "Dein Profil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Als Avatar verwenden"
#, fuzzy
msgid "Display name"
msgstr "Anzeigename"
@ -541,6 +548,15 @@ msgstr "Untertitel"
msgid "Content"
msgstr "Inhalt"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Hochladen"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr ""
@ -736,9 +752,16 @@ msgstr "Hochladen"
msgid "You don't have any media yet."
msgstr "Derzeit sind noch keine Mediendateien hochgeladen."
#, fuzzy
msgid "Content warning: {0}"
msgstr "Warnhinweis zum Inhalt"
msgid "Delete"
msgstr "Löschen"
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr "Hochladen von Mediendateien"

20
po/plume/en.po

@ -241,6 +241,12 @@ msgstr ""
msgid "Your Profile"
msgstr ""
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
msgid "Upload an avatar"
msgstr ""
# src/template_utils.rs:144
msgid "Display name"
msgstr ""
@ -518,6 +524,14 @@ msgstr ""
msgid "Content"
msgstr ""
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
msgid "Upload media"
msgstr ""
# src/template_utils.rs:144
msgid "Tags, separated by commas"
msgstr ""
@ -683,9 +697,15 @@ msgstr ""
msgid "You don't have any media yet."
msgstr ""
msgid "Content warning: {0}"
msgstr ""
msgid "Delete"
msgstr ""
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr ""

20
po/plume/es.po

@ -229,6 +229,12 @@ msgstr "Edita tu cuenta"
msgid "Your Profile"
msgstr "Tu perfil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
msgid "Upload an avatar"
msgstr ""
# src/template_utils.rs:144
msgid "Display name"
msgstr ""
@ -503,6 +509,14 @@ msgstr ""
msgid "Content"
msgstr "Contenido"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
msgid "Upload media"
msgstr ""
# src/template_utils.rs:144
msgid "Tags, separated by commas"
msgstr ""
@ -672,9 +686,15 @@ msgstr ""
msgid "You don't have any media yet."
msgstr ""
msgid "Content warning: {0}"
msgstr ""
msgid "Delete"
msgstr ""
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr ""

23
po/plume/fr.po

@ -247,6 +247,13 @@ msgstr "Modifier votre compte"
msgid "Your Profile"
msgstr "Votre profil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Utiliser comme avatar"
#, fuzzy
msgid "Display name"
msgstr "Nom affiché"
@ -536,6 +543,15 @@ msgstr "Sous-titre"
msgid "Content"
msgstr "Contenu"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Téléverser"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr ""
@ -730,9 +746,16 @@ msgstr "Téléverser"
msgid "You don't have any media yet."
msgstr "Vous n’avez pas encore de média."
#, fuzzy
msgid "Content warning: {0}"
msgstr "Avertissement"
msgid "Delete"
msgstr "Supprimer"
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr "Téléversement de média"

23
po/plume/gl.po

@ -247,6 +247,13 @@ msgstr "Edite a súa conta"
msgid "Your Profile"
msgstr "O seu perfil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Utilizar como avatar"
#, fuzzy
msgid "Display name"
msgstr "Nome mostrado"
@ -536,6 +543,15 @@ msgstr "Subtítulo"
msgid "Content"
msgstr "Contido"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Subir"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr ""
@ -726,9 +742,16 @@ msgstr "Subir"
msgid "You don't have any media yet."
msgstr "Aínda non ten medios"
#, fuzzy
msgid "Content warning: {0}"
msgstr "Aviso sobre o contido"
msgid "Delete"
msgstr "Eliminar"
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr "Subir medios"

23
po/plume/it.po

@ -247,6 +247,13 @@ msgstr "Modifica il tuo account"
msgid "Your Profile"
msgstr "Il tuo Profilo"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Usa come avatar"
#, fuzzy
msgid "Display name"
msgstr "Nome Visualizzato"
@ -539,6 +546,15 @@ msgstr "Sottotitolo"
msgid "Content"
msgstr "Contenuto"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Carica"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr ""
@ -733,9 +749,16 @@ msgstr "Carica"
msgid "You don't have any media yet."
msgstr "Non hai ancora nessun media."
#, fuzzy
msgid "Content warning: {0}"
msgstr "Avviso di contenuto sensibile"
msgid "Delete"
msgstr "Elimina"
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr "Caricamento di un media"

24
po/plume/ja.po

@ -240,6 +240,13 @@ msgstr "自分のアカウントを編集"
msgid "Your Profile"
msgstr "自分のプロフィール"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "アバターとして使う"
#, fuzzy
msgid "Display name"
msgstr "表示名"
@ -531,6 +538,15 @@ msgstr "サブタイトル"
msgid "Content"
msgstr "コメント"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "アップロード"
# src/template_utils.rs:144
msgid "Tags, separated by commas"
msgstr ""
@ -709,9 +725,17 @@ msgstr "アップロード"
msgid "You don't have any media yet."
msgstr "メディアがまだありません。"
#, fuzzy
msgid "Content warning: {0}"
msgstr "コンテンツの警告"
msgid "Delete"
msgstr "削除"
#, fuzzy
msgid "Details"
msgstr "メディアの詳細"
msgid "Media upload"
msgstr "メディアのアップロード"

22
po/plume/nb.po

@ -253,6 +253,12 @@ msgstr "Rediger kontoen din"
msgid "Your Profile"
msgstr "Din profil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
msgid "Upload an avatar"
msgstr ""
#, fuzzy
msgid "Display name"
msgstr "Visningsnavn"
@ -565,6 +571,15 @@ msgstr "Tittel"
msgid "Content"
msgstr "Innhold"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Din kommentar"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr ""
@ -748,9 +763,16 @@ msgstr ""
msgid "You don't have any media yet."
msgstr ""
#, fuzzy
msgid "Content warning: {0}"
msgstr "Innhold"
msgid "Delete"
msgstr ""
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr ""

24
po/plume/pl.po

@ -222,6 +222,13 @@ msgstr "Edytuj swoje konto"
msgid "Your Profile"
msgstr "Twój profil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Użyj jako awataru"
msgid "Display name"
msgstr "Nazwa wyświetlana"
@ -505,6 +512,15 @@ msgstr "Podtytuł"
msgid "Content"
msgstr "Zawartość"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Wyślij"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr "Tagi, oddzielone przecinkami"
@ -678,9 +694,17 @@ msgstr "Wyślij"
msgid "You don't have any media yet."
msgstr "Nie masz żadnej zawartości multimedialnej."
#, fuzzy
msgid "Content warning: {0}"
msgstr "Ostrzeżenie o zawartości"
msgid "Delete"
msgstr "Usuń"
#, fuzzy
msgid "Details"
msgstr "Szczegóły zawartości multimedialnej"
msgid "Media upload"
msgstr "Wysyłanie zawartości multimedialnej"

18
po/plume/plume.pot

@ -239,6 +239,12 @@ msgstr ""
msgid "Your Profile"
msgstr ""
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
msgid "Upload an avatar"
msgstr ""
# src/template_utils.rs:144
msgid "Display name"
msgstr ""
@ -510,6 +516,12 @@ msgstr ""
msgid "Content"
msgstr ""
msgid "You can upload medias to your gallery, and copy their Markdown code in your articles to insert them."
msgstr ""
msgid "Upload media"
msgstr ""
# src/template_utils.rs:144
msgid "Tags, separated by commas"
msgstr ""
@ -668,9 +680,15 @@ msgstr ""
msgid "You don't have any media yet."
msgstr ""
msgid "Content warning: {0}"
msgstr ""
msgid "Delete"
msgstr ""
msgid "Details"
msgstr ""
msgid "Media upload"
msgstr ""

24
po/plume/pt.po

@ -235,6 +235,13 @@ msgstr "Editar sua conta"
msgid "Your Profile"
msgstr "Seu Perfil"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Utilizar como avatar"
#, fuzzy
msgid "Display name"
msgstr "Nome exibido"
@ -526,6 +533,15 @@ msgstr "Subtítulo"
msgid "Content"
msgstr "Conteúdo"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Carregar"
# src/template_utils.rs:144
msgid "Tags, separated by commas"
msgstr ""
@ -701,9 +717,17 @@ msgstr "Carregar"
msgid "You don't have any media yet."
msgstr "Você ainda não tem nenhuma mídia."
#, fuzzy
msgid "Content warning: {0}"
msgstr "Alerta de conteúdo"
msgid "Delete"
msgstr "Suprimir"
#, fuzzy
msgid "Details"
msgstr "Detalhes da mídia"
msgid "Media upload"
msgstr "Carregamento de mídia"

24
po/plume/ru.po

@ -252,6 +252,13 @@ msgstr "Редактировать ваш аккаунт"
msgid "Your Profile"
msgstr "Ваш профиль"
msgid "To change your avatar, upload it in your gallery and select from there."
msgstr ""
#, fuzzy
msgid "Upload an avatar"
msgstr "Использовать как аватар"
#, fuzzy
msgid "Display name"
msgstr "Имя для отображения"
@ -543,6 +550,15 @@ msgstr "Подзаголовок"
msgid "Content"
msgstr "Содержимое"
msgid ""
"You can upload medias to your gallery, and copy their Markdown code in your "
"articles to insert them."
msgstr ""
#, fuzzy
msgid "Upload media"
msgstr "Загрузить"
# src/template_utils.rs:143
msgid "Tags, separated by commas"
msgstr ""
@ -740,9 +756,17 @@ msgstr "Загрузить"
msgid "You don't have any media yet."
msgstr "Пока что вы не можете загружать медиафайлы."
#, fuzzy
msgid "Content warning: {0}"
msgstr "Предупреждение о контенте"
msgid "Delete"
msgstr "Удалить"
#, fuzzy
msgid "Details"
msgstr "Детали медиафайла"
msgid "Media upload"
msgstr "Загрузка медиафайлов"

17
src/routes/medias.rs

@ -5,14 +5,17 @@ use rocket_i18n::I18n;
use std::fs;
use plume_models::{Error, db_conn::DbConn, medias::*, users::User};
use template_utils::Ructe;
use routes::errors::ErrorPage;
use routes::{Page, errors::ErrorPage};
#[get("/medias")]
pub fn list(user: User, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
let medias = Media::for_user(&*conn, user.id)?;
#[get("/medias?<page>")]
pub fn list(user: User, conn: DbConn, intl: I18n, page: Option<Page>) -> Result<Ructe, ErrorPage> {
let page = page.unwrap_or_default();
let medias = Media::page_for_user(&*conn, &user, page.limits())?;
Ok(render!(medias::index(
&(&*conn, &intl.catalog, Some(user)),
medias
&(&*conn, &intl.catalog, Some(user.clone())),
medias,
page.0,
Page::total(Media::count_for_user(&*conn, &user)? as i32)
)))
}
@ -109,7 +112,7 @@ pub fn delete(id: i32, user: User, conn: DbConn) -> Result<Redirect, ErrorPage>
if media.owner_id == user.id {
media.delete(&*conn)?;
}
Ok(Redirect::to(uri!(list)))
Ok(Redirect::to(uri!(list: page = _)))
}
#[post("/medias/<id>/avatar")]

28
static/css/_global.scss

@ -307,6 +307,10 @@ figure {
figcaption {
padding: 1em;
}
audio, video {
width: 100%;
}
}
.preview {
@ -318,6 +322,30 @@ figure {
margin-right: 20px;
}
.media-preview {
min-height: 8em;
&:not(.image) {
background-color: #7765E3;
background-repeat: no-repeat;
background-position: center;
background-size: 4em;
}
&.unknown {
background-image: url('/static/images/unknown-file.svg');
display: block;
}
&.audio {
background-image: url('/static/images/audio-file.svg');
}
&.video {
background-image: url('/static/images/video-file.svg');
}
}
/// Avatars
.avatar {
background-position: center;

65
static/images/audio-file.svg

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-headphones"
version="1.1"
id="svg6"
sodipodi:docname="audio-file.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M3 18v-6a9 9 0 0 1 18 0v6"
id="path2"
style="stroke:#ffffff;stroke-opacity:1" />
<path
d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"
id="path4"
style="stroke:#ffffff;stroke-opacity:1" />
</svg>

65
static/images/unknown-file.svg

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-file"
version="1.1"
id="svg6"
sodipodi:docname="unknown-file.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"
id="path2"
style="stroke:#ffffff;stroke-opacity:1" />
<polyline
points="13 2 13 9 20 9"
id="polyline4"
style="stroke:#ffffff;stroke-opacity:1" />
</svg>

115
static/images/video-file.svg

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-film"
version="1.1"
id="svg18"
sodipodi:docname="video-file.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata24">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs22" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview20"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg18" />
<rect
x="2"
y="2"
width="20"
height="20"
rx="2.18"
ry="2.18"
id="rect2"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="7"
y1="2"
x2="7"
y2="22"
id="line4"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="17"
y1="2"
x2="17"
y2="22"
id="line6"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="2"
y1="12"
x2="22"
y2="12"
id="line8"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="2"
y1="7"
x2="7"
y2="7"
id="line10"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="2"
y1="17"
x2="7"
y2="17"
id="line12"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="17"
y1="17"
x2="22"
y2="17"
id="line14"
style="stroke:#ffffff;stroke-opacity:1" />
<line
x1="17"
y1="7"
x2="22"
y2="7"
id="line16"
style="stroke:#ffffff;stroke-opacity:1" />
</svg>

2
templates/medias/details.rs.html

@ -9,7 +9,7 @@
@:base(ctx, i18n!(ctx.1, "Media details"), {}, {}, {
<h1>@i18n!(ctx.1, "Media details")</h1>
<section>
<a href="@uri!(medias::list)">@i18n!(ctx.1, "Go back to the gallery")</a>
<a href="@uri!(medias::list: page = _)">@i18n!(ctx.1, "Go back to the gallery")</a>
</section>
<section>

50
templates/medias/index.rs.html

@ -1,10 +1,9 @@
@use plume_models::medias::Media;
@use plume_models::safe_string::SafeString;
@use plume_models::medias::*;
@use templates::base;
@use template_utils::*;
@use routes::*;
@(ctx: BaseContext, medias: Vec<Media>)
@(ctx: BaseContext, medias: Vec<Media>, page: i32, n_pages: i32)
@:base(ctx, i18n!(ctx.1, "Your media"), {}, {}, {
<h1>@i18n!(ctx.1, "Your media")</h1>
@ -12,22 +11,33 @@
<a href="@uri!(medias::new)" class="inline-block button">@i18n!(ctx.1, "Upload")</a>
</div>
<section>
@if medias.is_empty() {
<p>@i18n!(ctx.1, "You don't have any media yet.")</p>
@if medias.is_empty() {
<p>@i18n!(ctx.1, "You don't have any media yet.")</p>
}
<div class="cards spaced">
@for media in medias {
<div class="card">
<div class="cover media-preview @media.category().to_string()"
@if media.category() == MediaCategory::Image {
style="background-image: url('@media.url(ctx.0).unwrap_or_default()')"
}
></div>
<main>
<p class="p-summary">@media.alt_text</p>
@if let Some(cw) = media.content_warning {
<p>@i18n!(ctx.1, "Content warning: {0}"; cw)</p>
}
</main>
<footer>
<form action="@uri!(medias::delete: id = media.id)" class="inline" method="POST">
<input type="submit" value="@i18n!(ctx.1, "Delete")"/>
</form>
&mdash;
<a href="@uri!(medias::details: id = media.id)">@i18n!(ctx.1, "Details")</a>
</footer>
</div>
}
<div class="list">
@for media in medias {
<div class="card flex">
@Html(media.preview_html(ctx.0).unwrap_or(SafeString::new("")))
<main class="grow">
<p><a href="@uri!(medias::details: id = media.id)">@media.alt_text</a></p>
</main>
<form action="@uri!(medias::delete: id = media.id)" class="inline" method="POST">
<input type="submit" value="@i18n!(ctx.1, "Delete")"/>
</form>
</div>
}
</div>
</section>
</div>
@paginate(ctx.1, page, n_pages)
})

4
templates/partials/post_card.rs.html

@ -16,7 +16,7 @@
<main>
<p class="p-summary">@article.subtitle</p>
</main>
<p class="author">
<footer class="authors">
@Html(i18n!(ctx.1, "By {0}"; format!(
"<a class=\"p-author h-card\" href=\"{}\">{}</a>",
uri!(user::details: name = article.get_authors(ctx.0).unwrap_or_default()[0].get_fqn(ctx.0)),
@ -29,6 +29,6 @@
@if !article.published {
⋅ @i18n!(ctx.1, "Draft")
}
</p>
</footer>
</div>

4
templates/posts/new.rs.html

@ -27,6 +27,10 @@
<label for="plume-editor">@i18n!(ctx.1, "Content")<small>@i18n!(ctx.1, "Markdown syntax is supported")</small></label>
<textarea id="plume-editor" name="content" rows="20">@form.content</textarea>
<small id="editor-left">@content_len</small>
<p>
@i18n!(ctx.1, "You can upload medias to your gallery, and copy their Markdown code in your articles to insert them.")
<a href="@uri!(medias::new)">@i18n!(ctx.1, "Upload media")</a>
</p>
@input!(ctx.1, tags (optional text), "Tags, separated by commas", form, errors.clone(), "")

2
templates/users/dashboard.rs.html

@ -37,6 +37,6 @@
<section>
<h2>@i18n!(ctx.1, "Your media")</h2>
<a class="button" href="@uri!(medias::list)">@i18n!(ctx.1, "Go to your gallery")</a>
<a class="button" href="@uri!(medias::list: page = _)">@i18n!(ctx.1, "Go to your gallery")</a>
</section>
})

4
templates/users/edit.rs.html

@ -9,6 +9,10 @@
@:base(ctx, i18n!(ctx.1, "Edit your account"), {}, {}, {
@if let Some(u) = ctx.2.clone() {
<h1>@i18n!(ctx.1, "Your Profile")</h1>
<p>
@i18n!(ctx.1, "To change your avatar, upload it in your gallery and select from there.")
<a href="@uri!(medias::new)">@i18n!(ctx.1, "Upload an avatar")</a>
</p>
<form method="post" action="@uri!(user::update: _name = u.username.clone())">
<!-- Rocket hack to use various HTTP methods -->
<input type=hidden name="_method" value="put">

Loading…
Cancel
Save