From b008e11fb046d7356712db4aef31f9b7be4915d1 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 29 Jun 2018 14:22:43 +0200 Subject: [PATCH 001/103] Add validator --- Cargo.lock | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 +++++---- src/main.rs | 3 +++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f20bc426..3e41543e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,6 +592,11 @@ dependencies = [ "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "if_chain" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "indexmap" version = "1.0.1" @@ -995,6 +1000,8 @@ dependencies = [ "rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)", "rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "validator_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "webfinger 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1183,6 +1190,18 @@ dependencies = [ "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex-syntax" version = "0.5.5" @@ -1191,6 +1210,14 @@ dependencies = [ "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex-syntax" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "relay" version = "0.1.1" @@ -1926,6 +1953,32 @@ dependencies = [ "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "validator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "validator_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "vcpkg" version = "0.2.3" @@ -2064,6 +2117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum hyper 0.11.25 (registry+https://github.com/rust-lang/crates.io-index)" = "549dbb86397490ce69d908425b9beebc85bbaad25157d67479d4995bb56fdf9a" "checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" +"checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8" "checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum isatty 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a118a53ba42790ef25c82bb481ecf36e2da892646cccd361e69a6bb881e19398" @@ -2128,7 +2182,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" +"checksum regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c93d55961981ba9226a213b385216f83ab43bd6ac53ab16b2eeb47e337cf4e" "checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb" +"checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "241faa9a8ca28a03cbbb9815a5d085f271d4c0168a19181f106aa93240c22ddb" @@ -2210,6 +2266,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" +"checksum validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4a8c44fecf027a477e70a86cd7f4863410adf120ca2cb13408cb099057b8e2d0" +"checksum validator_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "708ee89305635499f793d0e2dd9d0b1b5d00daba90fdfb1392b87c7279521fab" "checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index efcb4b83..265c4725 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,20 @@ gettext-rs = "0.4" heck = "0.3.0" rpassword = "2.0" serde_json = "1.0" +validator = "0.7" +validator_derive = "0.7" webfinger = "0.2" [dependencies.diesel] features = ["postgres", "r2d2", "chrono"] version = "*" -[dependencies.plume-models] -path = "plume-models" - [dependencies.plume-common] path = "plume-common" +[dependencies.plume-models] +path = "plume-models" + [dependencies.rocket] git = "https://github.com/SergioBenitez/Rocket" rev = "df7111143e466c18d1f56377a8d9530a5a306aba" @@ -45,4 +47,4 @@ git = "https://github.com/BaptisteGelez/rocket_i18n" rev = "5b4225d5bed5769482dc926a7e6d6b79f1217be6" [workspace] -members = ['plume-models', 'plume-common'] +members = ["plume-models", "plume-common"] diff --git a/src/main.rs b/src/main.rs index 6d55a5dc..bdf1387a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,9 @@ extern crate rocket_i18n; extern crate rpassword; #[macro_use] extern crate serde_json; +extern crate validator; +#[macro_use] +extern crate validator_derive; extern crate webfinger; use rocket_contrib::Template; From c81bb9ec2572e10da9c4e9ee6c87ea8f7c65a2d0 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 29 Jun 2018 14:56:00 +0200 Subject: [PATCH 002/103] Make forms validatable --- src/routes/blogs.rs | 13 ++++++++++++- src/routes/comments.rs | 5 +++-- src/routes/posts.rs | 13 ++++++++++++- src/routes/session.rs | 5 ++++- src/routes/user.rs | 16 +++++++++++++++- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 1e627439..0dc44838 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -5,6 +5,7 @@ use rocket::{ }; use rocket_contrib::Template; use serde_json; +use validator::{Validate, ValidationError}; use plume_common::activity_pub::ActivityStream; use plume_common::utils; @@ -49,11 +50,21 @@ fn new_auth() -> Flash{ utils::requires_login("You need to be logged in order to create a new blog", uri!(new)) } -#[derive(FromForm)] +#[derive(FromForm, Validate)] struct NewBlogForm { + #[validate(custom = "valid_slug")] pub title: String } +fn valid_slug(title: &str) -> Result<(), ValidationError> { + let slug = utils::make_actor_id(title.to_string()); + if slug.len() == 0 { + Err(ValidationError::new("empty_slug")) + } else { + Ok(()) + } +} + #[post("/blogs/new", data = "")] fn create(conn: DbConn, data: LenientForm, user: User) -> Redirect { let form = data.get(); diff --git a/src/routes/comments.rs b/src/routes/comments.rs index ee5112bb..78fd2852 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -3,6 +3,7 @@ use rocket::{ response::Redirect }; use serde_json; +use validator::Validate; use plume_common::activity_pub::broadcast; use plume_models::{ @@ -15,9 +16,10 @@ use plume_models::{ }; use inbox::Inbox; -#[derive(FromForm, Debug)] +#[derive(FromForm, Debug, Validate)] struct NewCommentForm { pub responding_to: Option, + #[validate(length(min = "1"))] pub content: String } @@ -26,7 +28,6 @@ fn create(blog_name: String, slug: String, data: LenientForm, us let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap(); let form = data.get(); - println!("form: {:?}", form); let (new_comment, id) = NewComment::build() .content(form.content.clone()) diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 9a518b25..a27b275b 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -4,6 +4,7 @@ use rocket::request::LenientForm; use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; +use validator::{Validate, ValidationError}; use plume_common::activity_pub::{broadcast, ActivityStream}; use plume_common::utils; @@ -81,13 +82,23 @@ fn new(blog: String, user: User, conn: DbConn) -> Template { } } -#[derive(FromForm)] +#[derive(FromForm, Validate)] struct NewPostForm { + #[validate(custom = "valid_slug")] pub title: String, pub content: String, pub license: String } +fn valid_slug(title: &str) -> Result<(), ValidationError> { + let slug = title.to_string().to_kebab_case(); + if slug.len() == 0 { + Err(ValidationError::new("empty_slug")) + } else { + Ok(()) + } +} + #[post("/~//new", data = "")] fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Redirect { let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); diff --git a/src/routes/session.rs b/src/routes/session.rs index c99548ba..1e033e90 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -5,6 +5,7 @@ use rocket::{ request::{LenientForm,FlashMessage} }; use rocket_contrib::Template; +use validator::{Validate, ValidationError}; use plume_models::{ db_conn::DbConn, @@ -32,9 +33,11 @@ fn new_message(user: Option, message: Message) -> Template { } -#[derive(FromForm)] +#[derive(FromForm, Validate)] struct LoginForm { + #[validate(length(min = "1"))] email_or_name: String, + #[validate(length(min = "8"))] password: String } diff --git a/src/routes/user.rs b/src/routes/user.rs index 3e47b8fc..0cc0576b 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -7,6 +7,7 @@ use rocket::{request::LenientForm, }; use rocket_contrib::Template; use serde_json; +use validator::{Validate, ValidationError}; use plume_common::activity_pub::{ ActivityStream, broadcast, Id, IntoId, @@ -157,14 +158,27 @@ fn update(_name: String, conn: DbConn, user: User, data: LenientForm Result<(), ValidationError> { + if form.password != form.password_confirmation { + Err(ValidationError::new("password_match")) + } else { + Ok(()) + } +} + #[post("/users/new", data = "")] fn create(conn: DbConn, data: LenientForm) -> Result { let form = data.get(); From d54fa51a6d4c11ba04ec1fb26825358fdc9c0e5d Mon Sep 17 00:00:00 2001 From: Matthieu Date: Sat, 30 Jun 2018 15:21:05 +0200 Subject: [PATCH 003/103] New forms style --- static/main.css | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/static/main.css b/static/main.css index 5f218a57..99a13a69 100644 --- a/static/main.css +++ b/static/main.css @@ -237,6 +237,11 @@ main .article-meta .comments > * { margin-left: 20%; margin-right: 20%; } font-weight: 600; } +/* New comment */ + +main .article-meta .comments form input[type="submit"] +{ font-size: 1em; } + /* Comment / Respond button */ main .article-meta .comments a.button:before { @@ -309,30 +314,39 @@ main .article-meta .comments .list { label { display: block; - margin: 1em 0; + margin: 1.5em auto; + font-size: 1.2em; + text-align: center; } input { transition: all 0.1s ease-in; display: block; width: 100%; - margin: auto auto 5em; - padding: 0.5em; + max-width: 40rem; + margin: auto; + padding: 1em; box-sizing: border-box; background: #F4F4F4; color: #242424; border: none; - border-bottom: solid #DADADA 2px; + border: solid #DADADA thin; + border-radius: 0.5em; + + font-size: 1.2em; + font-weight: 400; + text-align: center; } form input[type="submit"] { margin: 2em auto; } input:focus { background: #FAFAFA; - border-bottom-color: #7765E3; + border-color: #7765E3; } textarea { display: block; width: 100%; + max-width: 40rem; min-height: 4em; margin: auto; padding: 1em; @@ -356,7 +370,7 @@ textarea { display: inline-block; border-radius: 0.5em; - margin: 0.5em 0; + margin: 0.5em auto; padding: 0.75em 1em; background: transparent; @@ -365,6 +379,8 @@ textarea { cursor: pointer; } +input[type="submit"] +{ display: block; } .button:hover, input[type="submit"]:hover { background: #7765E399; color: white; @@ -375,6 +391,7 @@ textarea { */ form.new-post .title { + margin: 0 auto; padding: 0.75em 0; background: none; @@ -382,6 +399,7 @@ form.new-post .title { font-family: "Playfair Display", serif; font-size: 2em; + text-align: left; } form.new-post textarea { min-height: 8em; @@ -389,17 +407,19 @@ form.new-post textarea { background: none; } form.new-post input[type="submit"] { + transition: all 0.2s ease; display: block; margin: 1em auto; - width: 60%; - background: #DADADA; + background: #ECECEC; color: #242424; border: none; font-family: "Playfair Display", serif; font-size: 1.5em; } +form.new-post input[type="submit"]:hover +{ background: #DADADA; } /* * == User == From a391f1ae9d6f837828ab881accb9b572324d66e0 Mon Sep 17 00:00:00 2001 From: Dominik Pataky Date: Thu, 5 Jul 2018 21:58:32 +0200 Subject: [PATCH 004/103] Add German (de) translation --- po/LINGUAS | 1 + po/de.po | 284 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 po/de.po diff --git a/po/LINGUAS b/po/LINGUAS index 4fec581d..118575b7 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1,3 +1,4 @@ en fr pl +de diff --git a/po/de.po b/po/de.po new file mode 100644 index 00000000..be7c4289 --- /dev/null +++ b/po/de.po @@ -0,0 +1,284 @@ +msgid "" +msgstr "" +"Project-Id-Version: plume\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-06-15 16:33-0700\n" +"PO-Revision-Date: 2018-07-05 21:25+0200\n" +"Last-Translator: cookie \n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.8\n" + +msgid "Latest articles" +msgstr "Neueste Artikel" + +msgid "No posts to see here yet." +msgstr "Bisher keine Artikel vorhanden." + +msgid "New article" +msgstr "Neuer Artikel" + +msgid "New blog" +msgstr "Neuer Blog" + +msgid "Create a blog" +msgstr "Erstelle einen Blog" + +msgid "Title" +msgstr "Titel" + +msgid "Create blog" +msgstr "Blog erstellen" + +msgid "Comment \"{{ post }}\"" +msgstr "Kommentar \"{{ post }}\"" + +msgid "Content" +msgstr "Inhalt" + +msgid "Submit comment" +msgstr "Kommentar abschicken" + +msgid "Something broke on our side." +msgstr "Bei dir ist etwas schief gegangen." + +msgid "Sorry about that. If you think this is a bug, please report it." +msgstr "Entschuldige. Wenn du denkst einen Bug gefunden zu haben, kannst du diesen gerne melden." + +msgid "Configuration" +msgstr "Konfiguration" + +msgid "Configure your instance" +msgstr "Konfiguriere deine Instanz" + +msgid "Name" +msgstr "Name" + +msgid "Let's go!" +msgstr "Los geht's!" + +msgid "Welcome on {{ instance_name | escape }}" +msgstr "Willkommen auf {{ instance_name | escape }}" + +msgid "Notifications" +msgstr "Benachrichtigungen" + +msgid "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" +msgstr "Geschrieben von {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" + +msgid "This article is under the {{ license }} license." +msgstr "Dieser Artikel steht unter der {{ license }} Lizenz." + +msgid "One like" +msgid_plural "{{ count }} likes" +msgstr[0] "Ein Like" +msgstr[1] "{{ count }} Likes" + +msgid "I don't like this anymore" +msgstr "Nicht mehr Liken" + +msgid "Add yours" +msgstr "Like" + +msgid "One reshare" +msgid_plural "{{ count }} reshares" +msgstr[0] "Ein Reshare" +msgstr[1] "{{ count }} Reshares" + +msgid "I don't want to reshare this anymore" +msgstr "Nicht mehr Resharen" + +msgid "Reshare" +msgstr "Resharen" + +msgid "Comments" +msgstr "Kommentare" + +msgid "Respond" +msgstr "Antworten" + +msgid "Comment" +msgstr "Kommentar" + +msgid "New post" +msgstr "Neuer Beitrag" + +msgid "Create a post" +msgstr "Beitrag erstellen" + +msgid "Publish" +msgstr "Veröffentlichen" + +msgid "Login" +msgstr "Login" + +msgid "Username or email" +msgstr "Nutzername oder E-Mail" + +msgid "Password" +msgstr "Passwort" + +msgid "Dashboard" +msgstr "Dashboard" + +msgid "Your Dashboard" +msgstr "Dein Dashboard" + +msgid "Your Blogs" +msgstr "Deine Blogs" + +msgid "You don't have any blog yet. Create your own, or ask to join one." +msgstr "Du hast bisher keinen Blog. Erstelle deinen eigenen oder tritt einem Blog bei." + +msgid "Start a new blog" +msgstr "Starte einen neuen Blog" + +msgid "Admin" +msgstr "Admin" + +msgid "It is you" +msgstr "Das bist du" + +msgid "Edit your profile" +msgstr "Ändere dein Profil" + +msgid "Open on {{ instance_url }}" +msgstr "Öffnen auf {{ instance_url }}" + +msgid "Follow" +msgstr "Folgen" + +msgid "Unfollow" +msgstr "Entfolgen" + +msgid "Recently reshared" +msgstr "Vor Kurzem Reshared" + +msgid "One follower" +msgid_plural "{{ count }} followers" +msgstr[0] "Ein Follower" +msgstr[1] "{{ count }} Followers" + +msgid "Edit your account" +msgstr "Ändere deinen Account" + +msgid "Your Profile" +msgstr "Dein Profil" + +msgid "Display Name" +msgstr "Anzeigename" + +msgid "Email" +msgstr "E-Mail" + +msgid "Summary" +msgstr "Zusammenfassung" + +msgid "Update account" +msgstr "Account aktualisieren" + +msgid "{{ name | escape }}'s followers" +msgstr "{{ name | escape }}s Follower" + +msgid "Followers" +msgstr "Follower" + +msgid "New Account" +msgstr "Neuer Account" + +msgid "Create an account" +msgstr "Erstelle einen Account" + +msgid "Username" +msgstr "Nutzername" + +msgid "Password confirmation" +msgstr "Passwort Wiederholung" + +msgid "Create account" +msgstr "Account erstellen" + +msgid "Plume" +msgstr "Plume" + +msgid "Menu" +msgstr "Menü" + +msgid "My account" +msgstr "Mein Account" + +msgid "Log Out" +msgstr "Ausloggen" + +msgid "Log In" +msgstr "Einloggen" + +msgid "Register" +msgstr "Registrieren" + +msgid "You need to be logged in order to create a new blog" +msgstr "Du musst eingeloggt sein, um einen neuen Blog zu erstellen" + +msgid "You need to be logged in order to post a comment" +msgstr "Du musst eingeloggt sein, um einen Kommentar zu schreiben" + +msgid "You need to be logged in order to like a post" +msgstr "Du musst eingeloggt sein, um einen Beitrag zu Liken" + +msgid "You need to be logged in order to see your notifications" +msgstr "Du musst eingeloggt sein, um deine Benachrichtigungen zu sehen" + +msgid "You need to be logged in order to write a new post" +msgstr "Du musst eingeloggt sein, um einen neuen Beitrag zu schreiben" + +msgid "You need to be logged in order to reshare a post" +msgstr "Du musst eingeloggt sein, um einen Beitrag zu resharen" + +msgid "Invalid username or password" +msgstr "Nutzername oder Passwort ungültig" + +msgid "You need to be logged in order to access your dashboard" +msgstr "Du musst eingeloggt sein, um dein Dashboard zu sehen" + +msgid "You need to be logged in order to follow someone" +msgstr "Du musst eingeloggt sein, um jemandem zu folgen" + +msgid "You need to be logged in order to edit your profile" +msgstr "Du musst eingeloggt sein, um dein Profil zu editieren" + +msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" +msgstr "Von {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" + +msgid "{{ data }} reshared your article" +msgstr "{{ data }} hat deinen Artikel reshared" + +msgid "{{ data }} started following you" +msgstr "{{ data }} folgt dir nun" + +msgid "{{ data }} liked your article" +msgstr "{{ data }} hat deinen Artikel geliked" + +msgid "{{ data }} commented your article" +msgstr "{{ data }} hat deinen Artikel kommentiert" + +msgid "We couldn't find this page." +msgstr "Wir konnten diese Seite nicht finden." + +msgid "The link that led you here may be broken." +msgstr "Der Link, welcher dich hier her führte, ist wohl kaputt." + +msgid "You are not authorized." +msgstr "Nicht berechtigt." + +msgid "You are not author in this blog." +msgstr "Du bist kein Autor in diesem Blog." + +msgid "{{ data }} mentioned you." +msgstr "{{ data }} hat dich erwähnt." + +msgid "Your comment" +msgstr "Dein Kommentar" From 153400959c8900cf8e46275153c8cd98fcf75dd2 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 6 Jul 2018 11:51:19 +0200 Subject: [PATCH 005/103] Actually validate forms --- .../2018-04-22-093322_create_instances/up.sql | 2 +- src/routes/blogs.rs | 26 +++++++--- src/routes/comments.rs | 47 ++++++++++++----- src/routes/posts.rs | 31 +++++++++--- src/routes/session.rs | 50 ++++++++++--------- src/routes/user.rs | 39 +++++++-------- 6 files changed, 122 insertions(+), 73 deletions(-) diff --git a/migrations/2018-04-22-093322_create_instances/up.sql b/migrations/2018-04-22-093322_create_instances/up.sql index e6689b0f..46fd4a3c 100644 --- a/migrations/2018-04-22-093322_create_instances/up.sql +++ b/migrations/2018-04-22-093322_create_instances/up.sql @@ -1,4 +1,4 @@ --- Your SQL goes here +l-- Your SQL goes here CREATE TABLE instances ( id SERIAL PRIMARY KEY, local_domain VARCHAR NOT NULL, diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 0dc44838..e996e6c2 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -5,7 +5,7 @@ use rocket::{ }; use rocket_contrib::Template; use serde_json; -use validator::{Validate, ValidationError}; +use validator::{Validate, ValidationError, ValidationErrors}; use plume_common::activity_pub::ActivityStream; use plume_common::utils; @@ -66,15 +66,22 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> { } #[post("/blogs/new", data = "")] -fn create(conn: DbConn, data: LenientForm, user: User) -> Redirect { +fn create(conn: DbConn, data: LenientForm, user: User) -> Result { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); + let slug_taken_err = Blog::find_local(&*conn, slug.clone()).ok_or(ValidationError::new("existing_slug")); - if Blog::find_local(&*conn, slug.clone()).is_some() || slug.len() == 0 { - Redirect::to(uri!(new)) - } else { + let mut errors = match form.validate() { + Ok(_) => ValidationErrors::new(), + Err(e) => e + }; + if let Err(e) = slug_taken_err { + errors.add("title", e) + } + + if errors.is_empty() { let blog = Blog::insert(&*conn, NewBlog::new_local( - slug.to_string(), + slug.clone(), form.title.to_string(), String::from(""), Instance::local_id(&*conn) @@ -87,7 +94,12 @@ fn create(conn: DbConn, data: LenientForm, user: User) -> Redirect is_owner: true }); - Redirect::to(uri!(details: name = slug)) + Ok(Redirect::to(uri!(details: name = slug.clone()))) + } else { + Err(Template::render("blogs/new", json!({ + "account": user, + "errors": errors.inner() + }))) } } diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 78fd2852..5248e9b9 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -2,6 +2,7 @@ use rocket::{ request::LenientForm, response::Redirect }; +use rocket_contrib::Template; use serde_json; use validator::Validate; @@ -24,22 +25,44 @@ struct NewCommentForm { } #[post("/~///comment", data = "")] -fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn) -> Redirect { +fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn) -> Result { let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap(); let form = data.get(); + form.validate() + .map(|_| { + let (new_comment, id) = NewComment::build() + .content(form.content.clone()) + .in_response_to_id(form.responding_to.clone()) + .post(post.clone()) + .author(user.clone()) + .create(&*conn); - let (new_comment, id) = NewComment::build() - .content(form.content.clone()) - .in_response_to_id(form.responding_to.clone()) - .post(post) - .author(user.clone()) - .create(&*conn); + let instance = Instance::get_local(&*conn).unwrap(); + instance.received(&*conn, serde_json::to_value(new_comment.clone()).expect("JSON serialization error")) + .expect("We are not compatible with ourselve: local broadcast failed (new comment)"); + broadcast(&user, new_comment, user.get_followers(&*conn)); - let instance = Instance::get_local(&*conn).unwrap(); - instance.received(&*conn, serde_json::to_value(new_comment.clone()).expect("JSON serialization error")) - .expect("We are not compatible with ourselve: local broadcast failed (new comment)"); - broadcast(&user, new_comment, user.get_followers(&*conn)); + Redirect::to(format!("/~/{}/{}/#comment-{}", blog_name, slug, id)) + }) + .map_err(|errors| { + // TODO: de-duplicate this code + let comments = Comment::list_by_post(&*conn, post.id); - Redirect::to(format!("/~/{}/{}/#comment-{}", blog_name, slug, id)) + Template::render("posts/details", json!({ + "author": post.get_authors(&*conn)[0].to_json(&*conn), + "post": post, + "blog": blog, + "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(), + "n_likes": post.get_likes(&*conn).len(), + "has_liked": user.has_liked(&*conn, &post), + "n_reshares": post.get_reshares(&*conn).len(), + "has_reshared": user.has_reshared(&*conn, &post), + "account": user, + "date": &post.creation_date.timestamp(), + "previous": form.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn)), + "user_fqn": user.get_fqn(&*conn), + "errors": errors + })) + }) } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index a27b275b..7e6cb116 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -4,7 +4,7 @@ use rocket::request::LenientForm; use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; -use validator::{Validate, ValidationError}; +use validator::{Validate, ValidationError, ValidationErrors}; use plume_common::activity_pub::{broadcast, ActivityStream}; use plume_common::utils; @@ -94,22 +94,32 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> { let slug = title.to_string().to_kebab_case(); if slug.len() == 0 { Err(ValidationError::new("empty_slug")) + } else if slug == "new" { + Err(ValidationError::new("invalid_slug")) } else { Ok(()) } } #[post("/~//new", data = "")] -fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Redirect { +fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Result { let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); + let slug_taken_err = Blog::find_local(&*conn, slug.clone()).ok_or(ValidationError::new("existing_slug")); + + let mut errors = match form.validate() { + Ok(_) => ValidationErrors::new(), + Err(e) => e + }; + if let Err(e) = slug_taken_err { + errors.add("title", e) + } - if !user.is_author_in(&*conn, blog.clone()) { - Redirect::to(uri!(super::blogs::details: name = blog_name)) - } else { - if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() { - Redirect::to(uri!(new: blog = blog_name)) + if errors.is_empty() { + if !user.is_author_in(&*conn, blog.clone()) { + // actually it's not "Ok"… + Ok(Redirect::to(uri!(super::blogs::details: name = blog_name))) } else { let (content, mentions) = utils::md_to_html(form.content.to_string().as_ref()); @@ -135,7 +145,12 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D let act = post.create_activity(&*conn); broadcast(&user, act, user.get_followers(&*conn)); - Redirect::to(uri!(details: blog = blog_name, slug = slug)) + Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug))) } + } else { + Err(Template::render("posts/new", json!({ + "account": user, + "errors": errors.inner() + }))) } } diff --git a/src/routes/session.rs b/src/routes/session.rs index 1e033e90..4f5062ab 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -1,11 +1,10 @@ -use gettextrs::gettext; use rocket::{ http::{Cookie, Cookies, uri::Uri}, - response::{Redirect, status::NotFound}, + response::Redirect, request::{LenientForm,FlashMessage} }; use rocket_contrib::Template; -use validator::{Validate, ValidationError}; +use validator::{Validate, ValidationError, ValidationErrors}; use plume_models::{ db_conn::DbConn, @@ -42,28 +41,33 @@ struct LoginForm { } #[post("/login", data = "")] -fn create(conn: DbConn, data: LenientForm, flash: Option, mut cookies: Cookies) -> Result> { +fn create(conn: DbConn, data: LenientForm, flash: Option, mut cookies: Cookies) -> Result { let form = data.get(); - let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) { - Some(usr) => Ok(usr), - None => match User::find_local(&*conn, form.email_or_name.to_string()) { - Some(usr) => Ok(usr), - None => Err(gettext("Invalid username or password")) - } + let user = User::find_by_email(&*conn, form.email_or_name.to_string()) + .map(|u| Ok(u)) + .unwrap_or_else(|| User::find_local(&*conn, form.email_or_name.to_string()).map(|u| Ok(u)).unwrap_or(Err(()))); + + let mut errors = match form.validate() { + Ok(_) => ValidationErrors::new(), + Err(e) => e }; - match user { - Ok(usr) => { - if usr.auth(form.password.to_string()) { - cookies.add_private(Cookie::new(AUTH_COOKIE, usr.id.to_string())); - Ok(Redirect::to(Uri::new(flash - .and_then(|f| if f.name() == "callback" { Some(f.msg().to_owned()) } else { None }) - .unwrap_or("/".to_owned())) - )) - } else { - Err(NotFound(gettext("Invalid username or password"))) - } - }, - Err(e) => Err(NotFound(String::from(e))) + if let Err(_) = user.clone() { + errors.add("email_or_name", ValidationError::new("invalid_login")) + } else if !user.clone().expect("User not found").auth(form.password.clone()) { + errors.add("email_or_name", ValidationError::new("invalid_login")) + } + + if errors.is_empty() { + cookies.add_private(Cookie::new(AUTH_COOKIE, user.unwrap().id.to_string())); + Ok(Redirect::to(Uri::new(flash + .and_then(|f| if f.name() == "callback" { Some(f.msg().to_owned()) } else { None }) + .unwrap_or("/".to_owned())) + )) + } else { + Err(Template::render("session/login", json!({ + "account": user, + "errors": errors.inner() + }))) } } diff --git a/src/routes/user.rs b/src/routes/user.rs index 0cc0576b..d35a56cb 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -180,29 +180,24 @@ fn passwords_match(form: &NewUserForm) -> Result<(), ValidationError> { } #[post("/users/new", data = "")] -fn create(conn: DbConn, data: LenientForm) -> Result { +fn create(conn: DbConn, data: LenientForm) -> Result { let form = data.get(); - - if form.username.clone().len() < 1 { - Err(String::from("Username is required")) - } else if form.email.clone().len() < 1 { - Err(String::from("Email is required")) - } else if form.password.clone().len() < 8 { - Err(String::from("Password should be at least 8 characters long")) - } else if form.password == form.password_confirmation { - NewUser::new_local( - &*conn, - form.username.to_string(), - form.username.to_string(), - false, - String::from(""), - form.email.to_string(), - User::hash_pass(form.password.to_string()) - ).update_boxes(&*conn); - Ok(Redirect::to(uri!(super::session::new))) - } else { - Err(String::from("Passwords don't match")) - } + form.validate() + .map(|_| { + NewUser::new_local( + &*conn, + form.username.to_string(), + form.username.to_string(), + false, + String::from(""), + form.email.to_string(), + User::hash_pass(form.password.to_string()) + ).update_boxes(&*conn); + Redirect::to(uri!(super::session::new)) + }) + .map_err(|e| Template::render("users/new", json!({ + "errors": e.inner() + }))) } #[get("/@//outbox")] From 5f3afe900f303a558197a3be278223a329610c4f Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 6 Jul 2018 19:29:36 +0200 Subject: [PATCH 006/103] Display errors on invalid forms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will probably need a bit of styling… --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/main.rs | 3 +++ src/routes/blogs.rs | 9 ++++++--- src/routes/posts.rs | 11 +++++++---- src/routes/session.rs | 11 ++++++++--- src/routes/user.rs | 17 ++++++++++------- templates/blogs/new.html.tera | 5 +++-- templates/macros.html.tera | 9 +++++++++ templates/posts/new.html.tera | 15 +++++++++++---- templates/session/login.html.tera | 7 ++----- templates/users/new.html.tera | 16 +++++----------- 12 files changed, 68 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e41543e..3304accf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,6 +999,8 @@ dependencies = [ "rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=80687a64a8b9d44e4983e63cca6d707498e92fc7)", "rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)", "rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "validator_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 265c4725..a848910b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ failure = "0.1" gettext-rs = "0.4" heck = "0.3.0" rpassword = "2.0" +serde = "1.0" +serde_derive = "1.0" serde_json = "1.0" validator = "0.7" validator_derive = "0.7" diff --git a/src/main.rs b/src/main.rs index bdf1387a..215e2101 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,9 @@ extern crate rocket_contrib; extern crate rocket_csrf; extern crate rocket_i18n; extern crate rpassword; +extern crate serde; +#[macro_use] +extern crate serde_derive; #[macro_use] extern crate serde_json; extern crate validator; diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index e996e6c2..f7dd94a2 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -41,7 +41,9 @@ fn activity_details(name: String, conn: DbConn) -> ActivityStream { #[get("/blogs/new")] fn new(user: User) -> Template { Template::render("blogs/new", json!({ - "account": user + "account": user, + "errors": null, + "form": null })) } @@ -50,7 +52,7 @@ fn new_auth() -> Flash{ utils::requires_login("You need to be logged in order to create a new blog", uri!(new)) } -#[derive(FromForm, Validate)] +#[derive(FromForm, Validate, Serialize)] struct NewBlogForm { #[validate(custom = "valid_slug")] pub title: String @@ -98,7 +100,8 @@ fn create(conn: DbConn, data: LenientForm, user: User) -> Result Template { })) } else { Template::render("posts/new", json!({ - "account": user + "account": user, + "errors": null, + "form": null })) } } -#[derive(FromForm, Validate)] +#[derive(FromForm, Validate, Serialize)] struct NewPostForm { #[validate(custom = "valid_slug")] pub title: String, @@ -113,7 +115,7 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D Err(e) => e }; if let Err(e) = slug_taken_err { - errors.add("title", e) + errors.add("title", e); } if errors.is_empty() { @@ -150,7 +152,8 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D } else { Err(Template::render("posts/new", json!({ "account": user, - "errors": errors.inner() + "errors": errors.inner(), + "form": form }))) } } diff --git a/src/routes/session.rs b/src/routes/session.rs index 4f5062ab..e948d8e0 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -14,7 +14,9 @@ use plume_models::{ #[get("/login")] fn new(user: Option) -> Template { Template::render("session/login", json!({ - "account": user + "account": user, + "errors": null, + "form": null })) } @@ -27,7 +29,9 @@ struct Message { fn new_message(user: Option, message: Message) -> Template { Template::render("session/login", json!({ "account": user, - "message": message.m + "message": message.m, + "errors": null, + "form": null })) } @@ -66,7 +70,8 @@ fn create(conn: DbConn, data: LenientForm, flash: Option ActivityStream #[get("/users/new")] fn new(user: Option) -> Template { Template::render("users/new", json!({ - "account": user + "account": user, + "errors": null, + "form": null })) } @@ -158,16 +160,16 @@ fn update(_name: String, conn: DbConn, user: User, data: LenientForm) -> Result{{ "Create a blog" | _ }}
- - + {{ macros::input(name="title", label="Title", errors=errors, form=form) }} +
{% endblock content %} diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 6a10d1ee..dc6187f9 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -21,3 +21,12 @@

{% endmacro post_card %} +{% macro input(name, label, errors, form, type="text") %} + + {% if errors is defined and errors[name] %} + {% for err in errors[name] %} +

{{ err.message | _ }}

+ {% endfor %} + {% endif %} + +{% endmacro input %} diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera index 4cf457be..0878658b 100644 --- a/templates/posts/new.html.tera +++ b/templates/posts/new.html.tera @@ -7,11 +7,18 @@ {% block content %}

{{ "Create a post" | _ }}

- - + {{ macros::input(name="title", label="Title", errors=errors, form=form) }} - - + + {% if errors is defined and errors.content %} + {% for err in errors.content %} +

{{ err.message | _ }}

+ {% endfor %} + {% endif %} + + + + {{ macros::input(name="license", label="License", errors=errors, form=form) }}
diff --git a/templates/session/login.html.tera b/templates/session/login.html.tera index 4486bf3d..4a0ac027 100644 --- a/templates/session/login.html.tera +++ b/templates/session/login.html.tera @@ -10,11 +10,8 @@

{{ message }}

{% endif %}
- - - - - + {{ macros::input(name="email_or_name", label="Username or email", errors=errors, form=form) }} + {{ macros::input(name="password", label="Password", errors=errors, form=form, type="password") }}
diff --git a/templates/users/new.html.tera b/templates/users/new.html.tera index edf328c0..462d8a81 100644 --- a/templates/users/new.html.tera +++ b/templates/users/new.html.tera @@ -1,4 +1,5 @@ {% extends "base" %} +{% import "macros" as macros %} {% block title %} {{ "New Account" | _ }} @@ -7,17 +8,10 @@ {% block content %}

{{ "Create an account" | _ }}

- - - - - - - - - - - + {{ macros::input(name="username", label="Username", errors=errors, form=form) }} + {{ macros::input(name="email", label="Email", errors=errors, form=form, type="email") }} + {{ macros::input(name="password", label="Password", errors=errors, form=form, type="password") }} + {{ macros::input(name="password_confirmation", label="Password confirmation", errors=errors, form=form, type="password") }}
From e5c1b3259d425b57ff5cd49edb60f5b56c5dcc11 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 6 Jul 2018 21:59:17 +0200 Subject: [PATCH 007/103] Make LoginForm serializable --- src/routes/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/session.rs b/src/routes/session.rs index e948d8e0..655fc693 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -36,7 +36,7 @@ fn new_message(user: Option, message: Message) -> Template { } -#[derive(FromForm, Validate)] +#[derive(FromForm, Validate, Serialize)] struct LoginForm { #[validate(length(min = "1"))] email_or_name: String, From 3775d3a9c9b1fd2921c2f0010c63c0c6f585e1ab Mon Sep 17 00:00:00 2001 From: Bat Date: Sat, 7 Jul 2018 22:51:48 +0200 Subject: [PATCH 008/103] HTML validation + Actually associate messages to errors + Fix inverted behavior on new blog and post form --- src/routes/blogs.rs | 13 +++++++++---- src/routes/comments.rs | 2 +- src/routes/posts.rs | 12 ++++++++---- src/routes/session.rs | 4 ++-- src/routes/user.rs | 2 +- templates/blogs/new.html.tera | 2 +- templates/macros.html.tera | 6 +++--- templates/posts/new.html.tera | 6 +++--- templates/session/login.html.tera | 5 +++-- templates/users/new.html.tera | 6 +++--- 10 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index f7dd94a2..9a6fcb8c 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -5,6 +5,7 @@ use rocket::{ }; use rocket_contrib::Template; use serde_json; +use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; use plume_common::activity_pub::ActivityStream; @@ -54,7 +55,7 @@ fn new_auth() -> Flash{ #[derive(FromForm, Validate, Serialize)] struct NewBlogForm { - #[validate(custom = "valid_slug")] + #[validate(custom(function = "valid_slug", message = "Invalid name"))] pub title: String } @@ -71,14 +72,17 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> { fn create(conn: DbConn, data: LenientForm, user: User) -> Result { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); - let slug_taken_err = Blog::find_local(&*conn, slug.clone()).ok_or(ValidationError::new("existing_slug")); let mut errors = match form.validate() { Ok(_) => ValidationErrors::new(), Err(e) => e }; - if let Err(e) = slug_taken_err { - errors.add("title", e) + if let Some(_) = Blog::find_local(&*conn, slug.clone()) { + errors.add("title", ValidationError { + code: Cow::from("existing_slug"), + message: Some(Cow::from("A blog with the same name already exists.")), + params: HashMap::new() + }); } if errors.is_empty() { @@ -98,6 +102,7 @@ fn create(conn: DbConn, data: LenientForm, user: User) -> Result, - #[validate(length(min = "1"))] + #[validate(length(min = "1", message = "Your comment can't be empty"))] pub content: String } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 182e101f..30051042 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -4,6 +4,7 @@ use rocket::request::LenientForm; use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; +use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; use plume_common::activity_pub::{broadcast, ActivityStream}; @@ -86,7 +87,7 @@ fn new(blog: String, user: User, conn: DbConn) -> Template { #[derive(FromForm, Validate, Serialize)] struct NewPostForm { - #[validate(custom = "valid_slug")] + #[validate(custom(function = "valid_slug", message = "Invalid title"))] pub title: String, pub content: String, pub license: String @@ -108,14 +109,17 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); - let slug_taken_err = Blog::find_local(&*conn, slug.clone()).ok_or(ValidationError::new("existing_slug")); let mut errors = match form.validate() { Ok(_) => ValidationErrors::new(), Err(e) => e }; - if let Err(e) = slug_taken_err { - errors.add("title", e); + if let Some(_) = Post::find_by_slug(&*conn, slug.clone(), blog.id) { + errors.add("title", ValidationError { + code: Cow::from("existing_slug"), + message: Some(Cow::from("A post with the same title already exists.")), + params: HashMap::new() + }); } if errors.is_empty() { diff --git a/src/routes/session.rs b/src/routes/session.rs index 655fc693..fd79f057 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -38,9 +38,9 @@ fn new_message(user: Option, message: Message) -> Template { #[derive(FromForm, Validate, Serialize)] struct LoginForm { - #[validate(length(min = "1"))] + #[validate(length(min = "1", message = "We need an email or a username to identify you"))] email_or_name: String, - #[validate(length(min = "8"))] + #[validate(length(min = "8", message = "Your password should be at least 8 characters long"))] password: String } diff --git a/src/routes/user.rs b/src/routes/user.rs index f736901e..1b0f42e9 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -161,7 +161,7 @@ fn update(_name: String, conn: DbConn, user: User, data: LenientForm{{ "Create a blog" | _ }}
- {{ macros::input(name="title", label="Title", errors=errors, form=form) }} + {{ macros::input(name="title", label="Title", errors=errors, form=form, props='required minlength="1"') }}
diff --git a/templates/macros.html.tera b/templates/macros.html.tera index dc6187f9..868ee781 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -21,12 +21,12 @@

{% endmacro post_card %} -{% macro input(name, label, errors, form, type="text") %} +{% macro input(name, label, errors, form, type="text", props="") %} {% if errors is defined and errors[name] %} {% for err in errors[name] %} -

{{ err.message | _ }}

+

{{ err.message | default(value="Unknown error") }}

{% endfor %} {% endif %} - + {% endmacro input %} diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera index 0878658b..335e2396 100644 --- a/templates/posts/new.html.tera +++ b/templates/posts/new.html.tera @@ -1,4 +1,5 @@ {% extends "base" %} +{% import "macros" as macros %} {% block title %} {{ "New post" | _ }} @@ -9,14 +10,13 @@
{{ macros::input(name="title", label="Title", errors=errors, form=form) }} - {% if errors is defined and errors.content %} {% for err in errors.content %} -

{{ err.message | _ }}

+

{{ err.message | default(value="Unknown error") | _ }}

{% endfor %} {% endif %} - + {{ macros::input(name="license", label="License", errors=errors, form=form) }} diff --git a/templates/session/login.html.tera b/templates/session/login.html.tera index 4a0ac027..bbee8c98 100644 --- a/templates/session/login.html.tera +++ b/templates/session/login.html.tera @@ -1,4 +1,5 @@ {% extends "base" %} +{% import "macros" as macros %} {% block title %} {{ "Login" | _ }} @@ -10,8 +11,8 @@

{{ message }}

{% endif %} - {{ macros::input(name="email_or_name", label="Username or email", errors=errors, form=form) }} - {{ macros::input(name="password", label="Password", errors=errors, form=form, type="password") }} + {{ macros::input(name="email_or_name", label="Username or email", errors=errors, form=form, props='minlenght="1"') }} + {{ macros::input(name="password", label="Password", errors=errors, form=form, type="password", props='minlenght="8"') }}
diff --git a/templates/users/new.html.tera b/templates/users/new.html.tera index 462d8a81..b0aef4e1 100644 --- a/templates/users/new.html.tera +++ b/templates/users/new.html.tera @@ -8,10 +8,10 @@ {% block content %}

{{ "Create an account" | _ }}

- {{ macros::input(name="username", label="Username", errors=errors, form=form) }} + {{ macros::input(name="username", label="Username", errors=errors, form=form, props='minlenght="1"') }} {{ macros::input(name="email", label="Email", errors=errors, form=form, type="email") }} - {{ macros::input(name="password", label="Password", errors=errors, form=form, type="password") }} - {{ macros::input(name="password_confirmation", label="Password confirmation", errors=errors, form=form, type="password") }} + {{ macros::input(name="password", label="Password", errors=errors, form=form, type="password", props='minlenght="8"') }} + {{ macros::input(name="password_confirmation", label="Password confirmation", errors=errors, form=form, type="password", props='minlenght="8"') }}
From 06d590ff3b1c1087da93df94fbd0400a819af540 Mon Sep 17 00:00:00 2001 From: Bat Date: Sat, 7 Jul 2018 22:57:53 +0200 Subject: [PATCH 009/103] Make form errors i18nalizable --- po/plume.pot | 34 ++++++++++++++++++++++++++++++++++ templates/macros.html.tera | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/po/plume.pot b/po/plume.pot index e1599c75..ccb727e3 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -281,3 +281,37 @@ msgstr "" msgid "Your comment" msgstr "" + +msgid "Unknown error" +msgstr "" + +msgid "Invalid name" +msgstr "" + +msgid "A blog with the same name already exists." +msgstr "" + +msgid "Your comment can't be empty" +msgstr "" + +msgid "A post with the same title already exists." +msgstr "" + +msgid "We need an email or a username to identify you" +msgstr "" + +msgid "Your password should be at least 8 characters long" +msgstr "" + +msgid "Passwords are not matching" +msgstr "" + +msgid "Username can't be empty" +msgstr "" + +msgid "Invalid email" +msgstr "" + +msgid "Password should be at least 8 characters long" +msgstr "" + diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 868ee781..c12a6799 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -25,7 +25,7 @@ {% if errors is defined and errors[name] %} {% for err in errors[name] %} -

{{ err.message | default(value="Unknown error") }}

+

{{ err.message | default(value="Unknown error") | _ }}

{% endfor %} {% endif %} From f79f01a93f564e8cbd8eaaa8f00c0b450a7f30ac Mon Sep 17 00:00:00 2001 From: Bat Date: Sun, 8 Jul 2018 20:01:19 +0200 Subject: [PATCH 010/103] Accept both actor objects and links for Follows --- plume-models/src/follows.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index af3412f8..05c50a38 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -1,4 +1,4 @@ -use activitypub::{Actor, activity::{Accept, Follow as FollowAct}}; +use activitypub::{Actor, activity::{Accept, Follow as FollowAct}, actor::Person}; use diesel::{self, PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl}; use plume_common::activity_pub::{broadcast, Id, IntoId, inbox::{FromActivity, Notify, WithInbox}, sign::Signer}; @@ -55,7 +55,9 @@ impl Follow { impl FromActivity for Follow { fn from_activity(conn: &PgConnection, follow: FollowAct, _actor: Id) -> Follow { - let from = User::from_url(conn, follow.follow_props.actor.as_str().unwrap().to_string()).unwrap(); + let from_id = follow.follow_props.actor_link::().map(|l| l.into()) + .unwrap_or_else(|_| follow.follow_props.actor_object::().expect("No actor object (nor ID) on Follow").object_props.id_string().expect("No ID on actor on Follow")); + let from = User::from_url(conn, from_id).unwrap(); match User::from_url(conn, follow.follow_props.object.as_str().unwrap().to_string()) { Some(user) => Follow::accept_follow(conn, &from, &user, follow, from.id, user.id), None => { From 291e20cb94440718638cc08da7e2390b102dc58d Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 9 Jul 2018 15:11:02 +0200 Subject: [PATCH 011/103] Remove a "l" lost in a migration file --- migrations/2018-04-22-093322_create_instances/up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/2018-04-22-093322_create_instances/up.sql b/migrations/2018-04-22-093322_create_instances/up.sql index 46fd4a3c..e6689b0f 100644 --- a/migrations/2018-04-22-093322_create_instances/up.sql +++ b/migrations/2018-04-22-093322_create_instances/up.sql @@ -1,4 +1,4 @@ -l-- Your SQL goes here +-- Your SQL goes here CREATE TABLE instances ( id SERIAL PRIMARY KEY, local_domain VARCHAR NOT NULL, From 3796aa48c2b071a05c35c05791dc0012f9a28d31 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 9 Jul 2018 15:14:07 +0200 Subject: [PATCH 012/103] Document gettext installation At least for Ubuntu-like distros --- doc/PREREQUISITES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/PREREQUISITES.md b/doc/PREREQUISITES.md index a06aa4e2..a7007f86 100644 --- a/doc/PREREQUISITES.md +++ b/doc/PREREQUISITES.md @@ -85,3 +85,12 @@ Once you have `rustup` installed, make sure you have the `nightly` rust toolchain installed: $ rustup toolchain install nightly + +### Installing GetText + +GetText is the tool we use to manage translations. It will be needed at runtime, since we only compile +translation files when starting Plume. + +#### Ubuntu-like Linux + + $ sudo apt-get install gettext From 8e47219d82328da9ade82ba86c1c79fe0ed304c9 Mon Sep 17 00:00:00 2001 From: Bat Date: Mon, 9 Jul 2018 17:24:53 +0200 Subject: [PATCH 013/103] Add a ApRequest request guard to accept multiple ActivityPub header on one route --- plume-common/src/activity_pub/mod.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index 93c8b09a..b77d6ebf 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -2,9 +2,10 @@ use activitypub::{Activity, Actor, Object, Link}; use array_tool::vec::Uniq; use reqwest::Client; use rocket::{ + Outcome, http::Status, response::{Response, Responder}, - request::Request + request::{FromRequest, Request} }; use serde_json; @@ -59,6 +60,25 @@ impl<'r, O: Object> Responder<'r> for ActivityStream { } } +pub struct ApRequest; +impl<'a, 'r> FromRequest<'a, 'r> for ApRequest { + type Error = (); + + fn from_request(request: &'a Request<'r>) -> Outcome { + request.headers().get_one("Content-Type").map(|header| header.split(",").map(|ct| match ct { + "application/ld+json; profile=\"w3.org/ns/activitystreams\"" | + "application/ld+json;profile=\"w3.org/ns/activitystreams\"" | + "application/activity+json" | + "application/ld+json" => Outcome::Success(ApRequest), + _ => Outcome::Forward(()) + }).fold(Outcome::Forward(()), |out, ct| if out.is_success() { + out + } else { + ct + })).unwrap_or(Outcome::Forward(())) + } +} + pub fn broadcast(sender: &S, act: A, to: Vec) { let boxes = to.into_iter() .map(|u| u.get_shared_inbox_url().unwrap_or(u.get_inbox_url())) From b95e384ed7215255c4074ad84e4657256260c08e Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 11 Jul 2018 17:30:01 +0200 Subject: [PATCH 014/103] Use the ApRequest guard for routes that need it + Fix a few issues with its impl Also fixes some Rocket warnings! --- plume-common/src/activity_pub/mod.rs | 15 +++++--- po/de.po | 55 +++++++++++++++++++++++++--- po/en.po | 33 +++++++++++++++++ po/fr.po | 37 +++++++++++++++++++ po/pl.po | 37 +++++++++++++++++++ src/routes/blogs.rs | 6 +-- src/routes/posts.rs | 6 +-- src/routes/user.rs | 10 ++--- 8 files changed, 177 insertions(+), 22 deletions(-) diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index b77d6ebf..bd23d3ac 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -60,22 +60,25 @@ impl<'r, O: Object> Responder<'r> for ActivityStream { } } +#[derive(Clone)] pub struct ApRequest; impl<'a, 'r> FromRequest<'a, 'r> for ApRequest { type Error = (); fn from_request(request: &'a Request<'r>) -> Outcome { - request.headers().get_one("Content-Type").map(|header| header.split(",").map(|ct| match ct { - "application/ld+json; profile=\"w3.org/ns/activitystreams\"" | - "application/ld+json;profile=\"w3.org/ns/activitystreams\"" | + request.headers().get_one("Accept").map(|header| header.split(",").map(|ct| match ct.trim() { + // bool for Forward: true if found a valid Content-Type for Plume first (HTML), false otherwise + "application/ld+json; profile=\"https://w3.org/ns/activitystreams\"" | + "application/ld+json;profile=\"https://w3.org/ns/activitystreams\"" | "application/activity+json" | "application/ld+json" => Outcome::Success(ApRequest), - _ => Outcome::Forward(()) - }).fold(Outcome::Forward(()), |out, ct| if out.is_success() { + "text/html" => Outcome::Forward(true), + _ => Outcome::Forward(false) + }).fold(Outcome::Forward(false), |out, ct| if out.is_success() || (out.is_forward() && out.clone().forwarded().unwrap()) { out } else { ct - })).unwrap_or(Outcome::Forward(())) + }).map_forward(|_| ())).unwrap_or(Outcome::Forward(())) } } diff --git a/po/de.po b/po/de.po index be7c4289..ec86afe8 100644 --- a/po/de.po +++ b/po/de.po @@ -47,7 +47,9 @@ msgid "Something broke on our side." msgstr "Bei dir ist etwas schief gegangen." msgid "Sorry about that. If you think this is a bug, please report it." -msgstr "Entschuldige. Wenn du denkst einen Bug gefunden zu haben, kannst du diesen gerne melden." +msgstr "" +"Entschuldige. Wenn du denkst einen Bug gefunden zu haben, kannst du diesen " +"gerne melden." msgid "Configuration" msgstr "Konfiguration" @@ -67,8 +69,11 @@ msgstr "Willkommen auf {{ instance_name | escape }}" msgid "Notifications" msgstr "Benachrichtigungen" -msgid "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" -msgstr "Geschrieben von {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" +msgid "" +"Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" +msgstr "" +"Geschrieben von {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}" +"{{ link_3 }}" msgid "This article is under the {{ license }} license." msgstr "Dieser Artikel steht unter der {{ license }} Lizenz." @@ -132,7 +137,9 @@ msgid "Your Blogs" msgstr "Deine Blogs" msgid "You don't have any blog yet. Create your own, or ask to join one." -msgstr "Du hast bisher keinen Blog. Erstelle deinen eigenen oder tritt einem Blog bei." +msgstr "" +"Du hast bisher keinen Blog. Erstelle deinen eigenen oder tritt einem Blog " +"bei." msgid "Start a new blog" msgstr "Starte einen neuen Blog" @@ -251,7 +258,8 @@ msgid "You need to be logged in order to edit your profile" msgstr "Du musst eingeloggt sein, um dein Profil zu editieren" msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" -msgstr "Von {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" +msgstr "" +"Von {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" msgid "{{ data }} reshared your article" msgstr "{{ data }} hat deinen Artikel reshared" @@ -282,3 +290,40 @@ msgstr "{{ data }} hat dich erwähnt." msgid "Your comment" msgstr "Dein Kommentar" + +msgid "Unknown error" +msgstr "" + +#, fuzzy +msgid "Invalid name" +msgstr "Nutzername oder Passwort ungültig" + +msgid "A blog with the same name already exists." +msgstr "" + +#, fuzzy +msgid "Your comment can't be empty" +msgstr "Dein Kommentar" + +msgid "A post with the same title already exists." +msgstr "" + +msgid "We need an email or a username to identify you" +msgstr "" + +msgid "Your password should be at least 8 characters long" +msgstr "" + +#, fuzzy +msgid "Passwords are not matching" +msgstr "Passwort Wiederholung" + +#, fuzzy +msgid "Username can't be empty" +msgstr "Nutzername oder E-Mail" + +msgid "Invalid email" +msgstr "" + +msgid "Password should be at least 8 characters long" +msgstr "" diff --git a/po/en.po b/po/en.po index 8f35e7d6..7e3192fe 100644 --- a/po/en.po +++ b/po/en.po @@ -288,3 +288,36 @@ msgstr "" msgid "Your comment" msgstr "" + +msgid "Unknown error" +msgstr "" + +msgid "Invalid name" +msgstr "" + +msgid "A blog with the same name already exists." +msgstr "" + +msgid "Your comment can't be empty" +msgstr "" + +msgid "A post with the same title already exists." +msgstr "" + +msgid "We need an email or a username to identify you" +msgstr "" + +msgid "Your password should be at least 8 characters long" +msgstr "" + +msgid "Passwords are not matching" +msgstr "" + +msgid "Username can't be empty" +msgstr "" + +msgid "Invalid email" +msgstr "" + +msgid "Password should be at least 8 characters long" +msgstr "" diff --git a/po/fr.po b/po/fr.po index b256a813..9dc3ef85 100644 --- a/po/fr.po +++ b/po/fr.po @@ -291,3 +291,40 @@ msgstr "" #, fuzzy msgid "Your comment" msgstr "Envoyer le commentaire" + +msgid "Unknown error" +msgstr "" + +#, fuzzy +msgid "Invalid name" +msgstr "Nom d'utilisateur ou mot de passe invalide" + +msgid "A blog with the same name already exists." +msgstr "" + +#, fuzzy +msgid "Your comment can't be empty" +msgstr "Envoyer le commentaire" + +msgid "A post with the same title already exists." +msgstr "" + +msgid "We need an email or a username to identify you" +msgstr "" + +msgid "Your password should be at least 8 characters long" +msgstr "" + +#, fuzzy +msgid "Passwords are not matching" +msgstr "Confirmation du mot de passe" + +#, fuzzy +msgid "Username can't be empty" +msgstr "Nom d'utilisateur ou email" + +msgid "Invalid email" +msgstr "" + +msgid "Password should be at least 8 characters long" +msgstr "" diff --git a/po/pl.po b/po/pl.po index 48f21679..c639647b 100644 --- a/po/pl.po +++ b/po/pl.po @@ -295,5 +295,42 @@ msgstr "{{ data }} wspomniał o Tobie." msgid "Your comment" msgstr "Twój komentarz" +msgid "Unknown error" +msgstr "" + +#, fuzzy +msgid "Invalid name" +msgstr "Nieprawidłowa nazwa użytkownika lub hasło" + +msgid "A blog with the same name already exists." +msgstr "" + +#, fuzzy +msgid "Your comment can't be empty" +msgstr "Twój komentarz" + +msgid "A post with the same title already exists." +msgstr "" + +msgid "We need an email or a username to identify you" +msgstr "" + +msgid "Your password should be at least 8 characters long" +msgstr "" + +#, fuzzy +msgid "Passwords are not matching" +msgstr "Potwierdzenie hasła" + +#, fuzzy +msgid "Username can't be empty" +msgstr "Nazwa użytkownika lub adres e-mail" + +msgid "Invalid email" +msgstr "" + +msgid "Password should be at least 8 characters long" +msgstr "" + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 9a6fcb8c..1a2b0104 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -8,7 +8,7 @@ use serde_json; use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; -use plume_common::activity_pub::ActivityStream; +use plume_common::activity_pub::{ActivityStream, ApRequest}; use plume_common::utils; use plume_models::{ blog_authors::*, @@ -33,8 +33,8 @@ fn details(name: String, conn: DbConn, user: Option) -> Template { }) } -#[get("/~/", format = "application/activity+json", rank = 1)] -fn activity_details(name: String, conn: DbConn) -> ActivityStream { +#[get("/~/", rank = 1)] +fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let blog = Blog::find_local(&*conn, name).unwrap(); ActivityStream::new(blog.into_activity(&*conn)) } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 30051042..95d9886b 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -7,7 +7,7 @@ use serde_json; use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; -use plume_common::activity_pub::{broadcast, ActivityStream}; +use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest}; use plume_common::utils; use plume_models::{ blogs::*, @@ -55,8 +55,8 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option }) } -#[get("/~//", rank = 3, format = "application/activity+json")] -fn activity_details(blog: String, slug: String, conn: DbConn) -> ActivityStream
{ +#[get("/~//", rank = 3)] +fn activity_details(blog: String, slug: String, conn: DbConn, _ap: ApRequest) -> ActivityStream
{ let blog = Blog::find_by_fqn(&*conn, blog).unwrap(); let post = Post::find_by_slug(&*conn, slug, blog.id).unwrap(); diff --git a/src/routes/user.rs b/src/routes/user.rs index 1b0f42e9..8281a701 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -10,7 +10,7 @@ use serde_json; use validator::{Validate, ValidationError}; use plume_common::activity_pub::{ - ActivityStream, broadcast, Id, IntoId, + ActivityStream, broadcast, Id, IntoId, ApRequest, inbox::{Notify} }; use plume_common::utils; @@ -112,8 +112,8 @@ fn followers(name: String, conn: DbConn, account: Option) -> Template { }) } -#[get("/@/", format = "application/activity+json", rank = 1)] -fn activity_details(name: String, conn: DbConn) -> ActivityStream { +#[get("/@/", rank = 1)] +fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let user = User::find_local(&*conn, name).unwrap(); ActivityStream::new(user.into_activity(&*conn)) } @@ -222,8 +222,8 @@ fn inbox(name: String, conn: DbConn, data: String) -> String { } } -#[get("/@//followers", format = "application/activity+json")] -fn ap_followers(name: String, conn: DbConn) -> ActivityStream { +#[get("/@//followers")] +fn ap_followers(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let user = User::find_local(&*conn, name).unwrap(); let followers = user.get_followers(&*conn).into_iter().map(|f| Id::new(f.ap_url)).collect::>(); From b59299ad5aa81962f0c914d01bbf2beeee244799 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 11 Jul 2018 17:31:02 +0200 Subject: [PATCH 015/103] Remove the last build warning The host-meta route is only available in XML anyway, and having warning during is making people doubt of its validity. --- src/routes/well_known.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/well_known.rs b/src/routes/well_known.rs index 662fa469..937b79d7 100644 --- a/src/routes/well_known.rs +++ b/src/routes/well_known.rs @@ -17,7 +17,7 @@ fn nodeinfo() -> Content { }).to_string()) } -#[get("/.well-known/host-meta", format = "application/xml")] +#[get("/.well-known/host-meta")] fn host_meta() -> String { format!(r#" From 0e773de9ffe88fb7a913c406bba5184a3f8e820b Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 11 Jul 2018 17:37:36 +0200 Subject: [PATCH 016/103] Remove old configuration template --- templates/instance/configure.html.tera | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 templates/instance/configure.html.tera diff --git a/templates/instance/configure.html.tera b/templates/instance/configure.html.tera deleted file mode 100644 index fbc14458..00000000 --- a/templates/instance/configure.html.tera +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base" %} - -{% block title %} -{{ "Configuration" | _ }} -{% endblock title %} - -{% block content %} -

{{ "Configure your instance" | _ }}

-
- - - - -
-{% endblock content %} From 67dd577a273265051bd950d0f0d6f6f7877307f4 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 11 Jul 2018 21:27:47 +0200 Subject: [PATCH 017/103] Make like and share button HTML forms Fixes #88 --- src/routes/likes.rs | 4 ++-- src/routes/reshares.rs | 4 ++-- static/main.css | 39 ++++++++++++++----------------- templates/posts/details.html.tera | 16 ++++++------- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/routes/likes.rs b/src/routes/likes.rs index 3ccd0d6f..8126871b 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -10,7 +10,7 @@ use plume_models::{ users::User }; -#[get("/~///like")] +#[post("/~///like")] fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap(); @@ -34,7 +34,7 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)) } -#[get("/~///like", rank = 2)] +#[post("/~///like", rank = 2)] fn create_auth(blog: String, slug: String) -> Flash{ utils::requires_login("You need to be logged in order to like a post", uri!(create: blog = blog, slug = slug)) } diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index f83a40f3..67cf87a9 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -10,7 +10,7 @@ use plume_models::{ users::User }; -#[get("/~///reshare")] +#[post("/~///reshare")] fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap(); @@ -34,7 +34,7 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)) } -#[get("/~///reshare", rank=1)] +#[post("/~///reshare", rank=1)] fn create_auth(blog: String, slug: String) -> Flash { utils::requires_login("You need to be logged in order to reshare a post", uri!(create: blog = blog, slug = slug)) } diff --git a/static/main.css b/static/main.css index 5f218a57..b3c2fa8b 100644 --- a/static/main.css +++ b/static/main.css @@ -118,7 +118,7 @@ article img { /* Article.Meta */ -main .article-meta { +main .article-meta, main .article-meta button { padding: 0; font-size: 1.1em; margin-top: 10%; @@ -156,8 +156,8 @@ main .article-meta .reshares > p { font-size: 1.5em; } -main .article-meta .likes a.button, -main .article-meta .reshares a.button { +main .article-meta .likes button, +main .article-meta .reshares button { display: flex; flex-direction: column; align-items: center; @@ -170,12 +170,12 @@ main .article-meta .reshares a.button { } main .article-meta .likes > p, -main .article-meta .likes a.button:hover { color: #E92F2F; } +main .article-meta .likes button:hover { color: #E92F2F; } main .article-meta .reshares > p, -main .article-meta .reshares a.button:hover { color: #7765E3; } +main .article-meta .reshares button:hover { color: #7765E3; } -main .article-meta .likes a.button:before, -main .article-meta .reshares a.button:before { +main .article-meta .likes button i, +main .article-meta .reshares button i { transition: background 0.1s ease-in; display: flex; align-items: center; @@ -186,42 +186,39 @@ main .article-meta .reshares a.button:before { height: 2.5em; border-radius: 50%; - font-family: "Font Awesome 5 Free"; } -main .article-meta .likes a.button:before { - content: ""; +main .article-meta .likes button i { color: #E92F2F; border: solid #E92F2F thin; font-weight: 400; } -main .article-meta .likes a.button:hover:before { +main .article-meta .likes button:hover i { background: rgba(233, 47, 47, 0.15); } -main .article-meta .reshares a.button:before { - content: ""; +main .article-meta .reshares button i { color: #7765E3; border: solid #7765E3 thin; font-weight: 600; } -main .article-meta .reshares a.button:hover:before { +main .article-meta .reshares button:hover i { background: rgba(119, 101, 227, 0.15); } -main .article-meta .likes a.button.liked:before { background: #E92F2F; } -main .article-meta .likes a.button.liked:hover:before { +main .article-meta .likes button.liked i { background: #E92F2F; } +main .article-meta .likes button.liked:hover i { background: rgba(233, 47, 47, 0.25); color: #E92F2F; } -main .article-meta .reshares a.button.reshared:before { background: #7765E3; } -main .article-meta .reshares a.button.reshared:hover:before { +main .article-meta .reshares button.reshared i { background: #7765E3; } +main .article-meta .reshares button.reshared:hover i { background: rgba(119, 101, 227, 0.25); color: #7765E3; } -main .article-meta .likes a.button.liked:before, -main .article-meta .reshares a.button.reshared:before { +main .article-meta .likes button.liked i, +main .article-meta .reshares button.reshared i { color: #F4F4F4; font-weight: 900; } @@ -351,7 +348,7 @@ textarea { /* Button & Submit */ -.button, input[type="submit"] { +.button, input[type="submit"], button { transition: all 0.1s ease-in; display: inline-block; diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index 4cb19562..9ec45faf 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -37,24 +37,24 @@

{{ "This article is under the {{ license }} license." | _(license=post.license) }}

- -
+ +

{{ n_reshares }}

{% if has_reshared %} - {{ "I don't want to reshare this anymore" | _ }} + {% else %} - {{ "Reshare" | _ }} + {% endif %} -
+
From cf23360c4a51d98ae461d86b769ff91e4622c2d3 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 11 Jul 2018 22:11:31 +0200 Subject: [PATCH 018/103] Avoid panics when encountering an invalid mention --- plume-models/src/mentions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plume-models/src/mentions.rs b/plume-models/src/mentions.rs index ab2a9dbb..0d2e586c 100644 --- a/plume-models/src/mentions.rs +++ b/plume-models/src/mentions.rs @@ -63,8 +63,8 @@ impl Mention { } pub fn from_activity(conn: &PgConnection, ment: link::Mention, inside: i32, in_post: bool) -> Option { - let ap_url = ment.link_props.href_string().unwrap(); - let mentioned = User::find_by_ap_url(conn, ap_url).unwrap(); + let ap_url = ment.link_props.href_string().ok()?; + let mentioned = User::find_by_ap_url(conn, ap_url)?; if in_post { Post::get(conn, inside.clone().into()).map(|post| { From a68adba8841f9beea865561bd5c1779e58251370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Mon, 16 Jul 2018 11:26:18 +0200 Subject: [PATCH 019/103] i18n: Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- po/pl.po | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/po/pl.po b/po/pl.po index c639647b..6f5c7e5c 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: plume\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-06-15 16:33-0700\n" -"PO-Revision-Date: 2018-06-29 23:02+0200\n" +"PO-Revision-Date: 2018-07-16 11:26+0200\n" "Last-Translator: Marcin Mikołajczak \n" "Language-Team: none\n" "Language: pl\n" @@ -12,7 +12,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 2.0.8\n" +"X-Generator: Poedit 2.0.9\n" msgid "Latest articles" msgstr "Najnowsze artykuły" @@ -296,41 +296,38 @@ msgid "Your comment" msgstr "Twój komentarz" msgid "Unknown error" -msgstr "" +msgstr "Nieznany błąd" -#, fuzzy msgid "Invalid name" -msgstr "Nieprawidłowa nazwa użytkownika lub hasło" +msgstr "Nieprawidłowa nazwa" msgid "A blog with the same name already exists." -msgstr "" +msgstr "Blog o tej nazwie już istnieje." -#, fuzzy msgid "Your comment can't be empty" -msgstr "Twój komentarz" +msgstr "Twój komentarz nie może być pusty" msgid "A post with the same title already exists." -msgstr "" +msgstr "Wpis o tym tytule już istnieje." msgid "We need an email or a username to identify you" msgstr "" +"Potrzebujemy nazwy użytkownika lub adresu e-mail, aby Cię zidentyfikować" msgid "Your password should be at least 8 characters long" -msgstr "" +msgstr "Twoje hasło musi składać się przynajmniej z 8 znaków" -#, fuzzy msgid "Passwords are not matching" -msgstr "Potwierdzenie hasła" +msgstr "Hasła nie pasują do siebie" -#, fuzzy msgid "Username can't be empty" -msgstr "Nazwa użytkownika lub adres e-mail" +msgstr "Nazwa użytkownika nie może być pusta" msgid "Invalid email" -msgstr "" +msgstr "Nieprawidłowy adres e-mail" msgid "Password should be at least 8 characters long" -msgstr "" +msgstr "Hasło musi składać się z przynajmniej 8 znaków" #~ msgid "Logowanie" #~ msgstr "Zaloguj się" From d0a898a6e8847e03d14f127e9121b8226c72d574 Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Tue, 17 Jul 2018 12:10:55 +0200 Subject: [PATCH 020/103] Update french translation --- po/fr.po | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/po/fr.po b/po/fr.po index 9dc3ef85..8e96b96c 100644 --- a/po/fr.po +++ b/po/fr.po @@ -62,17 +62,15 @@ msgstr "Nom" msgid "Let's go!" msgstr "C'est parti !" -#, fuzzy msgid "Welcome on {{ instance_name | escape }}" -msgstr "Bienvenue sur {{ instance_name }}" +msgstr "Bienvenue sur {{ instance_name | escape }}" msgid "Notifications" msgstr "Notifications" -#, fuzzy msgid "" "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" -msgstr "Écrit par {{ link_1 }}{{ url }}{{ link_2 }}{{ name }}{{ link_3 }}" +msgstr "Écrit par {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" msgid "This article is under the {{ license }} license." msgstr "Cet article est placé sous la licence {{ license }}" @@ -187,12 +185,11 @@ msgstr "Description" msgid "Update account" msgstr "Mettre à jour mes informations" -#, fuzzy msgid "{{ name | escape }}'s followers" -msgstr "{{ count }} abonné⋅e" +msgstr "Les abonné⋅e⋅s de {{ name | escape }}" msgid "Followers" -msgstr "{{ count }} abonné⋅e" +msgstr "Abonné⋅e⋅s" msgid "New Account" msgstr "Nouveau compte" @@ -257,9 +254,8 @@ msgstr "Vous devez vous connecter pour suivre quelqu'un" msgid "You need to be logged in order to edit your profile" msgstr "Vous devez vous connecter pour modifier votre profil" -#, fuzzy msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" -msgstr "De {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name }}{{ link_4 }}" +msgstr "De {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" msgid "{{ data }} reshared your article" msgstr "{{ data }} a repartagé votre article" @@ -286,45 +282,40 @@ msgid "You are not author in this blog." msgstr "Vous n'êtes pas auteur dans ce blog." msgid "{{ data }} mentioned you." -msgstr "" +msgstr "{{ data }} vous a mentionné." -#, fuzzy msgid "Your comment" -msgstr "Envoyer le commentaire" +msgstr "Votre commentaire" msgid "Unknown error" -msgstr "" +msgstr "Erreur inconnue" -#, fuzzy msgid "Invalid name" -msgstr "Nom d'utilisateur ou mot de passe invalide" +msgstr "Nom invalide" msgid "A blog with the same name already exists." -msgstr "" +msgstr "Un blog avec le même nom existe déjà." -#, fuzzy msgid "Your comment can't be empty" -msgstr "Envoyer le commentaire" +msgstr "Votre commentaire ne peux pas être vide" msgid "A post with the same title already exists." -msgstr "" +msgstr "Un article avec le même titre existe déjà." msgid "We need an email or a username to identify you" -msgstr "" +msgstr "Nous avons besoin d'une adresse mail ou d'un nom d'utilisateur pour vous identifier." msgid "Your password should be at least 8 characters long" -msgstr "" +msgstr "Votre mot de passe doit faire au moins 8 caractères." -#, fuzzy msgid "Passwords are not matching" -msgstr "Confirmation du mot de passe" +msgstr "Les mots de passe ne correspondent pas." -#, fuzzy msgid "Username can't be empty" -msgstr "Nom d'utilisateur ou email" +msgstr "Le nom d'utilisateur ne peut pas être vide." msgid "Invalid email" -msgstr "" +msgstr "Adresse mail invalide" msgid "Password should be at least 8 characters long" -msgstr "" +msgstr "Le mot de passe doit faire au moins 8 caractères." From b963fab45f8a00e3dbe3f4c0aac6c3c1c81104ae Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Tue, 17 Jul 2018 12:12:12 +0200 Subject: [PATCH 021/103] Fix empty button When using the french translation --- po/fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/fr.po b/po/fr.po index 8e96b96c..5de44dde 100644 --- a/po/fr.po +++ b/po/fr.po @@ -151,7 +151,7 @@ msgid "Edit your profile" msgstr "Éditez votre profil" msgid "Open on {{ instance_url }}" -msgstr "Bienvenue sur {{ instance_name }}" +msgstr "Ouvrir sur {{ instance_url }}" msgid "Follow" msgstr "S'abonner" From 184e898360c876e8aaaae0f1f35990ae7b37fddd Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Tue, 17 Jul 2018 22:36:07 +0200 Subject: [PATCH 022/103] Fix diesel_cli version in INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 46f77b32..778c6775 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -14,7 +14,7 @@ cd Plume rustup toolchain install nightly rustup toolchain default nightly rustup update -cargo install diesel_cli --no-default-features --features postgres # we dont need to compile anything else than pgsql +cargo install diesel_cli --no-default-features --features postgres --version '=1.2.0' # we dont need to compile anything else than pgsql ``` ## Now, if you want to run postgresql on the same server: From cfca504592ac9a03c2e97cc180ea4928bf717ee0 Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Tue, 17 Jul 2018 22:36:47 +0200 Subject: [PATCH 023/103] Fix diesel_cli version in DEVELOPMENT.md --- DEVELOPMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index f5b71170..2555fe29 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -53,7 +53,7 @@ When you will launch Plume for the first time, it will setup the database by its To run migrations and correctly setup the database, Plume use the `diesel` CLI tool under the hood. Therefore you should install it before running Plume. If this was your time installing Rust, you will probably need to run that using `cargo`. `cargo` is installed with `rustc` so if you followed the earlier instructions it will already be available. ``` -cargo install diesel_cli +cargo install diesel_cli --version '=1.2.0' ``` #### Running Plume From e22bd7a5885ccd2dae4e75197079c5b8a2a8e7f9 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 14:14:57 +0200 Subject: [PATCH 024/103] Fix some issues with new form style --- static/main.css | 48 ++++++++++++----------------------- templates/posts/new.html.tera | 3 ++- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/static/main.css b/static/main.css index 35f642c2..a256af45 100644 --- a/static/main.css +++ b/static/main.css @@ -311,11 +311,11 @@ main .article-meta .comments .list { label { display: block; - margin: 1.5em auto; + margin: 2em auto 1em; font-size: 1.2em; - text-align: center; + max-width: 40rem; } -input { +input, textarea { transition: all 0.1s ease-in; display: block; width: 100%; @@ -332,34 +332,22 @@ input { font-size: 1.2em; font-weight: 400; - text-align: center; } form input[type="submit"] { margin: 2em auto; } -input:focus { +input:focus, textarea:focus { background: #FAFAFA; border-color: #7765E3; } textarea { - display: block; - width: 100%; - max-width: 40rem; - min-height: 4em; - margin: auto; - padding: 1em; - box-sizing: border-box; - - background: #ECECEC; - color: #242424; - border: none; - resize: vertical; - - font-family: "Lora", serif; - font-size: 1.1em; - line-height: 1.5em; - text-align: justify; + resize: vertical; + + font-family: "Lora", serif; + font-size: 1.1em; + line-height: 1.5em; + text-align: justify; } - + /* Button & Submit */ .button, input[type="submit"], button { @@ -376,8 +364,7 @@ textarea { cursor: pointer; } -input[type="submit"] -{ display: block; } +input[type="submit"] { display: block; } .button:hover, input[type="submit"]:hover { background: #7765E399; color: white; @@ -399,14 +386,12 @@ form.new-post .title { text-align: left; } form.new-post textarea { - min-height: 8em; - padding: 0; - background: none; + min-height: 20em; } form.new-post input[type="submit"] { - transition: all 0.2s ease; + /*transition: all 0.2s ease; display: block; - margin: 1em auto; + margin: 1em auto;*/ background: #ECECEC; color: #242424; @@ -415,8 +400,7 @@ form.new-post input[type="submit"] { font-family: "Playfair Display", serif; font-size: 1.5em; } -form.new-post input[type="submit"]:hover -{ background: #DADADA; } +form.new-post input[type="submit"]:hover { background: #DADADA; } /* * == User == diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera index 335e2396..f42f2e15 100644 --- a/templates/posts/new.html.tera +++ b/templates/posts/new.html.tera @@ -16,7 +16,8 @@ {% endfor %} {% endif %} - + + {{ macros::input(name="license", label="License", errors=errors, form=form) }} From eef9e6b7eab4116c110dd1428cbba6e2a1f4f5c3 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 15:34:18 +0200 Subject: [PATCH 025/103] Better style for form errors --- static/main.css | 15 +++++++++++---- templates/posts/new.html.tera | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/static/main.css b/static/main.css index a256af45..830da762 100644 --- a/static/main.css +++ b/static/main.css @@ -1,3 +1,5 @@ +/* color palette: https://coolors.co/23f0c7-ef767a-7765e3-6457a6-ffe347 */ + @import url('/static/fonts/Route159/Route159.css'); @import url('/static/fonts/Lora/Lora.css'); @import url('/static/fonts/Playfair_Display/PlayfairDisplay.css'); @@ -370,6 +372,15 @@ input[type="submit"] { display: block; } color: white; } +/* Errors */ + +p.error { + color: #ef767a; + font-weight: bold; + max-width: 40rem; + margin: 1em auto; +} + /* * == New post == */ @@ -389,10 +400,6 @@ form.new-post textarea { min-height: 20em; } form.new-post input[type="submit"] { - /*transition: all 0.2s ease; - display: block; - margin: 1em auto;*/ - background: #ECECEC; color: #242424; border: none; diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera index f42f2e15..e4298b53 100644 --- a/templates/posts/new.html.tera +++ b/templates/posts/new.html.tera @@ -8,7 +8,7 @@ {% block content %}

{{ "Create a post" | _ }}

- {{ macros::input(name="title", label="Title", errors=errors, form=form) }} + {{ macros::input(name="title", label="Title", errors=errors, form=form, props="required") }} {% if errors is defined and errors.content %} {% for err in errors.content %} From 62007d17ee6d9b15972f696b0f2c0c850ca93585 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 15:35:01 +0200 Subject: [PATCH 026/103] Fix a visual bug when a post was reshared --- templates/posts/details.html.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index 9ec45faf..5d9cc1f2 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -50,7 +50,7 @@

{{ n_reshares }}

{% if has_reshared %} - + {% else %} {% endif %} From 389ad28d1491127f19eef5b843a6a9478b93323c Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 15:49:13 +0200 Subject: [PATCH 027/103] Don't broadcast activities to local users Fix #80 --- plume-common/src/activity_pub/inbox.rs | 2 ++ plume-common/src/activity_pub/mod.rs | 1 + plume-models/src/blogs.rs | 4 ++++ plume-models/src/users.rs | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/plume-common/src/activity_pub/inbox.rs b/plume-common/src/activity_pub/inbox.rs index 66855ff5..6ce93c10 100644 --- a/plume-common/src/activity_pub/inbox.rs +++ b/plume-common/src/activity_pub/inbox.rs @@ -38,4 +38,6 @@ pub trait WithInbox { fn get_inbox_url(&self) -> String; fn get_shared_inbox_url(&self) -> Option; + + fn is_local(&self) -> bool; } diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index bd23d3ac..841335f3 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -84,6 +84,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for ApRequest { pub fn broadcast(sender: &S, act: A, to: Vec) { let boxes = to.into_iter() + .filter(|u| !u.is_local()) .map(|u| u.get_shared_inbox_url().unwrap_or(u.get_inbox_url())) .collect::>() .unique(); diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 842eaff9..736a1fc0 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -257,6 +257,10 @@ impl WithInbox for Blog { fn get_shared_inbox_url(&self) -> Option { None } + + fn is_local(&self) -> bool { + self.instance_id == 0 + } } impl sign::Signer for Blog { diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index f3e41bde..333081e4 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -421,6 +421,10 @@ impl WithInbox for User { fn get_shared_inbox_url(&self) -> Option { self.shared_inbox_url.clone() } + + fn is_local(&self) -> bool { + self.instance_id == 0 + } } impl Signer for User { From 488bd929c5911718f19f9c30798f3ad1d909e7db Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 16:02:21 +0200 Subject: [PATCH 028/103] Try to fix the Hubzilla issue (#107) again --- src/inbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inbox.rs b/src/inbox.rs index 5081357f..3c020fc3 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -16,7 +16,7 @@ use plume_models::{ pub trait Inbox { fn received(&self, conn: &PgConnection, act: serde_json::Value) -> Result<(), Error> { - let actor_id = Id::new(act["actor"].as_str().unwrap()); + let actor_id = Id::new(act["actor"].as_str().unwrap_or(act["actor"]["id"].as_str().expect("No actor ID for incoming activity"))); match act["type"].as_str() { Some(t) => { match t { From e3a7eadb78ee074dc3e9d04a93ca10e9093ddebd Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 16:03:52 +0200 Subject: [PATCH 029/103] Do it lazily -_- --- src/inbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inbox.rs b/src/inbox.rs index 3c020fc3..d5b2dcef 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -16,7 +16,7 @@ use plume_models::{ pub trait Inbox { fn received(&self, conn: &PgConnection, act: serde_json::Value) -> Result<(), Error> { - let actor_id = Id::new(act["actor"].as_str().unwrap_or(act["actor"]["id"].as_str().expect("No actor ID for incoming activity"))); + let actor_id = Id::new(act["actor"].as_str().unwrap_or_else(|| act["actor"]["id"].as_str().expect("No actor ID for incoming activity"))); match act["type"].as_str() { Some(t) => { match t { From 3d436c10b1dd99170007412dcda85eac9afab594 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 16:25:02 +0200 Subject: [PATCH 030/103] Accept more content types when making AP requests --- plume-common/src/activity_pub/mod.rs | 1 + plume-common/src/activity_pub/request.rs | 3 ++- plume-models/src/blogs.rs | 4 ++-- plume-models/src/users.rs | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index 841335f3..2f5e068b 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -17,6 +17,7 @@ pub mod sign; pub const CONTEXT_URL: &'static str = "https://www.w3.org/ns/activitystreams"; pub const PUBLIC_VISIBILTY: &'static str = "https://www.w3.org/ns/activitystreams#Public"; +pub const AP_ACCEPT_HEADER: &'static str = "application/ld+json; profile=\"https://w3.org/ns/activitystreams\", application/ld+json;profile=\"https://w3.org/ns/activitystreams\", application/activity+json, application/ld+json"; pub fn context() -> serde_json::Value { json!([ diff --git a/plume-common/src/activity_pub/request.rs b/plume-common/src/activity_pub/request.rs index 401bb000..b00e0868 100644 --- a/plume-common/src/activity_pub/request.rs +++ b/plume-common/src/activity_pub/request.rs @@ -9,6 +9,7 @@ use std::{ time::SystemTime }; +use activity_pub::AP_ACCEPT_HEADER; use activity_pub::sign::Signer; const USER_AGENT: &'static str = "Plume/0.1.0"; @@ -25,7 +26,7 @@ pub fn headers() -> Headers { let mut headers = Headers::new(); headers.set(UserAgent::new(USER_AGENT)); headers.set(Date(SystemTime::now().into())); - headers.set(ContentType(Mime::from_str("application/activity+json").unwrap())); + headers.set(ContentType(Mime::from_str(AP_ACCEPT_HEADER).unwrap())); headers } diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 736a1fc0..1032f4d7 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -18,7 +18,7 @@ use webfinger::*; use {BASE_URL, USE_HTTPS}; use plume_common::activity_pub::{ - ApSignature, ActivityStream, Id, IntoId, PublicKey, + AP_ACCEPT_HEADER, ApSignature, ActivityStream, Id, IntoId, PublicKey, inbox::WithInbox, sign }; @@ -109,7 +109,7 @@ impl Blog { fn fetch_from_url(conn: &PgConnection, url: String) -> Option { let req = Client::new() .get(&url[..]) - .header(Accept(vec![qitem("application/activity+json".parse::().unwrap())])) + .header(Accept(vec![qitem(AP_ACCEPT_HEADER.parse::().unwrap())])) .send(); match req { Ok(mut res) => { diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 333081e4..9d66d2c7 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -13,7 +13,7 @@ use openssl::{ sign }; use plume_common::activity_pub::{ - ActivityStream, Id, IntoId, ApSignature, PublicKey, + AP_ACCEPT_HEADER, ActivityStream, Id, IntoId, ApSignature, PublicKey, inbox::WithInbox, sign::{Signer, gen_keypair} }; @@ -155,7 +155,7 @@ impl User { fn fetch_from_url(conn: &PgConnection, url: String) -> Option { let req = Client::new() .get(&url[..]) - .header(Accept(vec![qitem("application/activity+json".parse::().unwrap())])) + .header(Accept(vec![qitem(AP_ACCEPT_HEADER.parse::().unwrap())])) .send(); match req { Ok(mut res) => { From 2b04b39f5d8eac1f61644b0d8d47f299b3290d76 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 16:58:28 +0200 Subject: [PATCH 031/103] Correctly parse HTTP Accept headers --- plume-common/src/activity_pub/mod.rs | 10 +++++++++- plume-common/src/activity_pub/request.rs | 6 +++--- plume-models/src/blogs.rs | 4 ++-- plume-models/src/users.rs | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index 2f5e068b..626bdc18 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -17,7 +17,15 @@ pub mod sign; pub const CONTEXT_URL: &'static str = "https://www.w3.org/ns/activitystreams"; pub const PUBLIC_VISIBILTY: &'static str = "https://www.w3.org/ns/activitystreams#Public"; -pub const AP_ACCEPT_HEADER: &'static str = "application/ld+json; profile=\"https://w3.org/ns/activitystreams\", application/ld+json;profile=\"https://w3.org/ns/activitystreams\", application/activity+json, application/ld+json"; + +pub fn ap_accept_header() -> Vec<&'static str> { + vec![ + "application/ld+json; profile=\"https://w3.org/ns/activitystreams\"", + "application/ld+json;profile=\"https://w3.org/ns/activitystreams\"", + "application/activity+json", + "application/ld+json" + ] +} pub fn context() -> serde_json::Value { json!([ diff --git a/plume-common/src/activity_pub/request.rs b/plume-common/src/activity_pub/request.rs index b00e0868..05e24cbb 100644 --- a/plume-common/src/activity_pub/request.rs +++ b/plume-common/src/activity_pub/request.rs @@ -2,14 +2,14 @@ use base64; use openssl::hash::{Hasher, MessageDigest}; use reqwest::{ mime::Mime, - header::{ContentType, Date, Headers, UserAgent} + header::{Accept, Date, Headers, UserAgent, qitem} }; use std::{ str::FromStr, time::SystemTime }; -use activity_pub::AP_ACCEPT_HEADER; +use activity_pub::ap_accept_header; use activity_pub::sign::Signer; const USER_AGENT: &'static str = "Plume/0.1.0"; @@ -26,7 +26,7 @@ pub fn headers() -> Headers { let mut headers = Headers::new(); headers.set(UserAgent::new(USER_AGENT)); headers.set(Date(SystemTime::now().into())); - headers.set(ContentType(Mime::from_str(AP_ACCEPT_HEADER).unwrap())); + headers.set(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::().expect("Invalid Content-Type"))).collect())); headers } diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 1032f4d7..bd2a0a31 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -18,7 +18,7 @@ use webfinger::*; use {BASE_URL, USE_HTTPS}; use plume_common::activity_pub::{ - AP_ACCEPT_HEADER, ApSignature, ActivityStream, Id, IntoId, PublicKey, + ap_accept_header, ApSignature, ActivityStream, Id, IntoId, PublicKey, inbox::WithInbox, sign }; @@ -109,7 +109,7 @@ impl Blog { fn fetch_from_url(conn: &PgConnection, url: String) -> Option { let req = Client::new() .get(&url[..]) - .header(Accept(vec![qitem(AP_ACCEPT_HEADER.parse::().unwrap())])) + .header(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::().expect("Invalid Content-Type"))).collect())) .send(); match req { Ok(mut res) => { diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 9d66d2c7..1175c1eb 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -13,7 +13,7 @@ use openssl::{ sign }; use plume_common::activity_pub::{ - AP_ACCEPT_HEADER, ActivityStream, Id, IntoId, ApSignature, PublicKey, + ap_accept_header, ActivityStream, Id, IntoId, ApSignature, PublicKey, inbox::WithInbox, sign::{Signer, gen_keypair} }; @@ -155,7 +155,7 @@ impl User { fn fetch_from_url(conn: &PgConnection, url: String) -> Option { let req = Client::new() .get(&url[..]) - .header(Accept(vec![qitem(AP_ACCEPT_HEADER.parse::().unwrap())])) + .header(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::().expect("Invalid Content-Type"))).collect())) .send(); match req { Ok(mut res) => { From 16124e890e34d64616d398a2d13398c9407019b6 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 18:35:50 +0200 Subject: [PATCH 032/103] Add some test for mentions --- plume-common/src/utils.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plume-common/src/utils.rs b/plume-common/src/utils.rs index 8b14959c..364a3912 100644 --- a/plume-common/src/utils.rs +++ b/plume-common/src/utils.rs @@ -68,3 +68,26 @@ pub fn md_to_html(md: &str) -> (String, Vec) { html::push_html(&mut buf, parser); (buf, mentions.collect()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mentions() { + let tests = vec![ + ("nothing", vec![]), + ("@mention", vec!["mention"]), + ("@mention@instance.tld", vec!["mention@instance.tld"]), + ("@many @mentions", vec!["many", "mentions"]), + ("@start with a mentions", vec!["start"]), + ("mention at @end", vec!["end"]), + ("between parenthesis (@test)", vec!["test"]), + ("with some punctuation @test!", vec!["test"]), + ]; + + for (md, mentions) in tests { + assert_eq!(md_to_html(md).1, mentions.into_iter().map(|s| s.to_string()).collect::>()); + } + } +} From 6b58dcfda54be951971dacefcc062a25b3553f8c Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 19:00:49 +0200 Subject: [PATCH 033/103] Fix a bug in mentions Fixes #98 --- plume-common/src/utils.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plume-common/src/utils.rs b/plume-common/src/utils.rs index 364a3912..8e1acbaa 100644 --- a/plume-common/src/utils.rs +++ b/plume-common/src/utils.rs @@ -28,10 +28,15 @@ pub fn md_to_html(md: &str) -> (String, Vec) { Event::Text(txt) => { let (evts, _, _, _, new_mentions) = txt.chars().fold((vec![], false, String::new(), 0, vec![]), |(mut events, in_mention, text_acc, n, mut mentions), c| { if in_mention { - if (c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_') && (n < (txt.chars().count() - 1)) { + let char_matches = c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_'; + if char_matches && (n < (txt.chars().count() - 1)) { (events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions) } else { - let mention = text_acc + c.to_string().as_ref(); + let mention = if char_matches { + text_acc + c.to_string().as_ref() + } else { + text_acc + }; let short_mention = mention.clone(); let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or(""); let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into()); @@ -84,6 +89,7 @@ mod tests { ("mention at @end", vec!["end"]), ("between parenthesis (@test)", vec!["test"]), ("with some punctuation @test!", vec!["test"]), + (" @spaces ", vec!["spaces"]), ]; for (md, mentions) in tests { From 493fe731d0f3a82ec40614d206f849120f0eeaaf Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 19:02:31 +0200 Subject: [PATCH 034/103] Remove a debug message while we are at it --- plume-models/src/comments.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index a76b1018..260118eb 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -75,7 +75,6 @@ impl Comment { let mentions = Mention::list_for_comment(conn, self.id).into_iter() .map(|m| m.get_mentioned(conn).map(|u| u.get_fqn(conn)).unwrap_or(String::new())) .collect::>(); - println!("{:?}", mentions); json["mentions"] = serde_json::to_value(mentions).unwrap(); json } From ce256d6e3919d50ae0641cf4beeea7fa251e2367 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 23:08:49 +0200 Subject: [PATCH 035/103] List authors of a blog Update french translation accordingly Fixes #129 --- plume-models/src/blogs.rs | 10 ++++++++++ po/de.po | 6 ++++++ po/en.po | 5 +++++ po/fr.po | 12 ++++++++++-- po/pl.po | 7 +++++++ po/plume.pot | 4 ++++ src/routes/blogs.rs | 9 ++++++--- templates/blogs/details.html.tera | 11 +++++++++++ 8 files changed, 59 insertions(+), 5 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index bd2a0a31..7123d36a 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -23,6 +23,7 @@ use plume_common::activity_pub::{ sign }; use instance::*; +use users::User; use schema::blogs; pub type CustomGroup = CustomObject; @@ -68,6 +69,15 @@ impl Blog { Instance::get(conn, self.instance_id).expect("Couldn't find instance") } + pub fn list_authors(&self, conn: &PgConnection) -> Vec { + use schema::blog_authors; + use schema::users; + let authors_ids = blog_authors::table.filter(blog_authors::blog_id.eq(self.id)).select(blog_authors::author_id); + users::table.filter(users::id.eq(any(authors_ids))) + .load::(conn) + .expect("Couldn't load authors of a blog") + } + pub fn find_for_author(conn: &PgConnection, author_id: i32) -> Vec { use schema::blog_authors; let author_ids = blog_authors::table.filter(blog_authors::author_id.eq(author_id)).select(blog_authors::blog_id); diff --git a/po/de.po b/po/de.po index ec86afe8..5a870f9a 100644 --- a/po/de.po +++ b/po/de.po @@ -327,3 +327,9 @@ msgstr "" msgid "Password should be at least 8 characters long" msgstr "" + +#, fuzzy +msgid "One author in this blog: " +msgid_plural "{{ count }} authors in this blog: " +msgstr[0] "Du bist kein Autor in diesem Blog." +msgstr[1] "Du bist kein Autor in diesem Blog." diff --git a/po/en.po b/po/en.po index 7e3192fe..ea0cd335 100644 --- a/po/en.po +++ b/po/en.po @@ -321,3 +321,8 @@ msgstr "" msgid "Password should be at least 8 characters long" msgstr "" + +msgid "One author in this blog: " +msgid_plural "{{ count }} authors in this blog: " +msgstr[0] "" +msgstr[1] "" diff --git a/po/fr.po b/po/fr.po index 5de44dde..4d8f0db7 100644 --- a/po/fr.po +++ b/po/fr.po @@ -70,7 +70,8 @@ msgstr "Notifications" msgid "" "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" -msgstr "Écrit par {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" +msgstr "" +"Écrit par {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" msgid "This article is under the {{ license }} license." msgstr "Cet article est placé sous la licence {{ license }}" @@ -303,7 +304,9 @@ msgid "A post with the same title already exists." msgstr "Un article avec le même titre existe déjà." msgid "We need an email or a username to identify you" -msgstr "Nous avons besoin d'une adresse mail ou d'un nom d'utilisateur pour vous identifier." +msgstr "" +"Nous avons besoin d'une adresse mail ou d'un nom d'utilisateur pour vous " +"identifier." msgid "Your password should be at least 8 characters long" msgstr "Votre mot de passe doit faire au moins 8 caractères." @@ -319,3 +322,8 @@ msgstr "Adresse mail invalide" msgid "Password should be at least 8 characters long" msgstr "Le mot de passe doit faire au moins 8 caractères." + +msgid "One author in this blog: " +msgid_plural "{{ count }} authors in this blog: " +msgstr[0] "{{ count }} aut⋅eur⋅rice dans ce blog : " +msgstr[1] "{{ count }} aut⋅eur⋅rice⋅s dans ce blog : " diff --git a/po/pl.po b/po/pl.po index 6f5c7e5c..63ecaa83 100644 --- a/po/pl.po +++ b/po/pl.po @@ -329,5 +329,12 @@ msgstr "Nieprawidłowy adres e-mail" msgid "Password should be at least 8 characters long" msgstr "Hasło musi składać się z przynajmniej 8 znaków" +#, fuzzy +msgid "One author in this blog: " +msgid_plural "{{ count }} authors in this blog: " +msgstr[0] "Nie jesteś autorem tego bloga." +msgstr[1] "Nie jesteś autorem tego bloga." +msgstr[2] "Nie jesteś autorem tego bloga." + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/po/plume.pot b/po/plume.pot index ccb727e3..a5c0d95c 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -315,3 +315,7 @@ msgstr "" msgid "Password should be at least 8 characters long" msgstr "" +msgid "One author in this blog: " +msgid_plural "{{ count }} authors in this blog: " +msgstr[0] "" +msgstr[1] "" diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 1a2b0104..1a42f405 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -23,12 +23,15 @@ use plume_models::{ fn details(name: String, conn: DbConn, user: Option) -> Template { may_fail!(user, Blog::find_by_fqn(&*conn, name), "Requested blog couldn't be found", |blog| { let recents = Post::get_recents_for_blog(&*conn, &blog, 5); + let authors = &blog.list_authors(&*conn); Template::render("blogs/details", json!({ - "blog": blog, + "blog": &blog, "account": user, - "is_author": user.map(|x| x.is_author_in(&*conn, blog)), - "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>() + "is_author": user.map(|x| x.is_author_in(&*conn, blog.clone())), + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), + "authors": authors.into_iter().map(|u| u.to_json(&*conn)).collect::>(), + "n_authors": authors.len() })) }) } diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index ed9994a9..40ddf629 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -8,6 +8,17 @@ {% block content %}

{{ blog.title }} (~{{ blog.actor_id }})

{{ blog.summary }}

+

+ {{ "{{ count }} authors in this blog: " | _n(singular="One author in this blog: ", count = n_authors) }} + {% for author in authors %} + {% if author.display_name %} + {% set name = author.display_name %} + {% else %} + {% set name = author.username %} + {% endif %} + {{ name }}{% if not loop.last %},{% endif %} + {% endfor %} +

{{ "Latest articles" | _ }}

From a785cb987b7cd36e8d0cf696033c4b2be138ef5d Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 23:18:26 +0200 Subject: [PATCH 036/103] Left-align articles Fixes #119 --- static/main.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/main.css b/static/main.css index 830da762..124c37ff 100644 --- a/static/main.css +++ b/static/main.css @@ -111,7 +111,6 @@ main article { font-family: "Lora", serif; font-size: 1.2em; line-height: 1.7em; - text-align: justify; } article img { @@ -347,7 +346,6 @@ textarea { font-family: "Lora", serif; font-size: 1.1em; line-height: 1.5em; - text-align: justify; } /* Button & Submit */ From 782955e2c40b033df73f4654b9620088182ee0fe Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 18 Jul 2018 23:37:05 +0200 Subject: [PATCH 037/103] Hide social button when not connected Fixes #143 --- po/de.po | 3 +++ po/en.po | 3 +++ po/fr.po | 3 +++ po/pl.po | 3 +++ po/plume.pot | 3 +++ static/main.css | 7 ++++++ templates/posts/details.html.tera | 40 +++++++++++++++++-------------- 7 files changed, 44 insertions(+), 18 deletions(-) diff --git a/po/de.po b/po/de.po index 5a870f9a..9269fb9e 100644 --- a/po/de.po +++ b/po/de.po @@ -333,3 +333,6 @@ msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " msgstr[0] "Du bist kein Autor in diesem Blog." msgstr[1] "Du bist kein Autor in diesem Blog." + +msgid "Login or use your Fediverse account to interact with this article" +msgstr "" diff --git a/po/en.po b/po/en.po index ea0cd335..a53fb120 100644 --- a/po/en.po +++ b/po/en.po @@ -326,3 +326,6 @@ msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " msgstr[0] "" msgstr[1] "" + +msgid "Login or use your Fediverse account to interact with this article" +msgstr "" diff --git a/po/fr.po b/po/fr.po index 4d8f0db7..5721c9fe 100644 --- a/po/fr.po +++ b/po/fr.po @@ -327,3 +327,6 @@ msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " msgstr[0] "{{ count }} aut⋅eur⋅rice dans ce blog : " msgstr[1] "{{ count }} aut⋅eur⋅rice⋅s dans ce blog : " + +msgid "Login or use your Fediverse account to interact with this article" +msgstr "Connectez-vous ou utilisez votre compte sur le Fediverse pour interagir avec cet article" diff --git a/po/pl.po b/po/pl.po index 63ecaa83..937bb924 100644 --- a/po/pl.po +++ b/po/pl.po @@ -336,5 +336,8 @@ msgstr[0] "Nie jesteś autorem tego bloga." msgstr[1] "Nie jesteś autorem tego bloga." msgstr[2] "Nie jesteś autorem tego bloga." +msgid "Login or use your Fediverse account to interact with this article" +msgstr "" + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/po/plume.pot b/po/plume.pot index a5c0d95c..c604ef14 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -319,3 +319,6 @@ msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " msgstr[0] "" msgstr[1] "" + +msgid "Login or use your Fediverse account to interact with this article" +msgstr "" diff --git a/static/main.css b/static/main.css index 124c37ff..541f1ac7 100644 --- a/static/main.css +++ b/static/main.css @@ -22,6 +22,13 @@ a, a:visited { outline: none; } +.center { + text-align: center; + font-weight: bold; + opacity: 0.6; + padding: 5em; +} + /* * == Header == */ diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index 5d9cc1f2..baf0d8e2 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -36,26 +36,30 @@ From 67eb41add17b93ed273ff0668a14b366b0fabfa9 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 20 Jul 2018 18:42:35 +0200 Subject: [PATCH 044/103] Add pagination to the blog page No UI to control it yet --- plume-models/src/posts.rs | 9 +++++++ src/main.rs | 1 + src/routes/blogs.rs | 14 ++++++++--- src/routes/mod.rs | 42 +++++++++++++++++++++++++++++-- templates/blogs/details.html.tera | 4 +-- 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 856ec349..ecacedb0 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -91,6 +91,15 @@ impl Post { .expect("Error loading recent posts for blog") } + pub fn blog_page(conn: &PgConnection, blog: &Blog, (min, max): (i32, i32)) -> Vec { + posts::table.filter(posts::blog_id.eq(blog.id)) + .order(posts::creation_date.desc()) + .offset(min.into()) + .limit((max - min).into()) + .load::(conn) + .expect("Error loading a page of posts for blog") + } + pub fn get_authors(&self, conn: &PgConnection) -> Vec { use schema::users; use schema::post_authors; diff --git a/src/main.rs b/src/main.rs index 215e2101..c414e503 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ fn main() { let pool = setup::check(); rocket::ignite() .mount("/", routes![ + routes::blogs::paginated_details, routes::blogs::details, routes::blogs::activity_details, routes::blogs::outbox, diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 1a42f405..9aa07141 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -18,24 +18,30 @@ use plume_models::{ posts::Post, users::User }; +use routes::Page; -#[get("/~/", rank = 2)] -fn details(name: String, conn: DbConn, user: Option) -> Template { +#[get("/~/?", rank = 2)] +fn paginated_details(name: String, conn: DbConn, user: Option, page: Page) -> Template { may_fail!(user, Blog::find_by_fqn(&*conn, name), "Requested blog couldn't be found", |blog| { - let recents = Post::get_recents_for_blog(&*conn, &blog, 5); + let posts = Post::blog_page(&*conn, &blog, page.limits()); let authors = &blog.list_authors(&*conn); Template::render("blogs/details", json!({ "blog": &blog, "account": user, "is_author": user.map(|x| x.is_author_in(&*conn, blog.clone())), - "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), + "posts": posts.into_iter().map(|p| p.to_json(&*conn)).collect::>(), "authors": authors.into_iter().map(|u| u.to_json(&*conn)).collect::>(), "n_authors": authors.len() })) }) } +#[get("/~/", rank = 3)] +fn details(name: String, conn: DbConn, user: Option) -> Template { + paginated_details(name, conn, user, Page::first()) +} + #[get("/~/", rank = 1)] fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let blog = Blog::find_local(&*conn, name).unwrap(); diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 735cd4a1..8bfd4cf0 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,5 +1,11 @@ -use rocket::response::NamedFile; -use std::path::{Path, PathBuf}; +use rocket::{ + http::uri::{FromUriParam, UriDisplay}, + response::NamedFile +}; +use std::{ + fmt, + path::{Path, PathBuf} +}; macro_rules! may_fail { ($account:expr, $expr:expr, $template:expr, $msg:expr, | $res:ident | $block:block) => { @@ -28,6 +34,38 @@ macro_rules! may_fail { }; } +const ITEMS_PER_PAGE: i32 = 10; + +#[derive(FromForm)] +pub struct Page { + page: i32 +} + +impl UriDisplay for Page { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "page={}", &self.page as &UriDisplay) + } +} + +impl FromUriParam for Page { + type Target = Page; + fn from_uri_param(num: i32) -> Page { + Page { page: num } + } +} + +impl Page { + pub fn first() -> Page { + Page { + page: 1 + } + } + + pub fn limits(&self) -> (i32, i32) { + ((self.page - 1) * ITEMS_PER_PAGE, self.page * ITEMS_PER_PAGE) + } +} + pub mod blogs; pub mod comments; pub mod errors; diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index 96d805c1..abe983b2 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -22,14 +22,14 @@

{{ "Latest articles" | _ }}

- {% if recents | length < 1 %} + {% if posts | length < 1 %}

{{ "No posts to see here yet." | _ }}

{% endif %} {% if is_author %} {{ "New article" | _ }} {% endif %}
- {% for article in recents %} + {% for article in posts %} {{ macros::post_card(article=article) }} {% endfor %}
From 114a5295bb0b88d791ac06ab48e5705ac7e9ff65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Fri, 20 Jul 2018 20:56:04 +0200 Subject: [PATCH 045/103] i18n: Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- po/pl.po | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/po/pl.po b/po/pl.po index e57f447a..69163662 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: plume\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-06-15 16:33-0700\n" -"PO-Revision-Date: 2018-07-16 11:26+0200\n" +"PO-Revision-Date: 2018-07-20 20:55+0200\n" "Last-Translator: Marcin Mikołajczak \n" "Language-Team: none\n" "Language: pl\n" @@ -329,18 +329,19 @@ msgstr "Nieprawidłowy adres e-mail" msgid "Password should be at least 8 characters long" msgstr "Hasło musi składać się z przynajmniej 8 znaków" -#, fuzzy msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " -msgstr[0] "Nie jesteś autorem tego bloga." -msgstr[1] "Nie jesteś autorem tego bloga." -msgstr[2] "Nie jesteś autorem tego bloga." +msgstr[0] "Ten blog ma jednego autora: " +msgstr[1] "Ten blog ma {{ count }} autorów: " +msgstr[2] "Ten blog ma {{ count }} autorów: " msgid "Login or use your Fediverse account to interact with this article" msgstr "" +"Zaloguj się lub użyj konta w Fediwersum, aby wejść w interakcje z tym " +"artykułem" msgid "Optional" -msgstr "" +msgstr "Nieobowiązkowe" #~ msgid "Logowanie" #~ msgstr "Zaloguj się" From cd24b0f057c065e468ae4fc7ff910d07f2c0e447 Mon Sep 17 00:00:00 2001 From: "Kevin \"Ilphrin\" Pellet" Date: Sat, 21 Jul 2018 16:58:30 +0200 Subject: [PATCH 046/103] Show the total number of article on a blog fixes #150 --- plume-models/src/posts.rs | 6 ++++++ src/routes/blogs.rs | 4 +++- templates/blogs/details.html.tera | 5 ++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 856ec349..5a04b9f8 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -91,6 +91,12 @@ impl Post { .expect("Error loading recent posts for blog") } + pub fn get_for_blog(conn: &PgConnection, blog:&Blog) -> Vec { + posts::table.filter(posts::blog_id.eq(blog.id)) + .load::(conn) + .expect("Error loading posts for blog") + } + pub fn get_authors(&self, conn: &PgConnection) -> Vec { use schema::users; use schema::post_authors; diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 1a42f405..6ce0a874 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -23,6 +23,7 @@ use plume_models::{ fn details(name: String, conn: DbConn, user: Option) -> Template { may_fail!(user, Blog::find_by_fqn(&*conn, name), "Requested blog couldn't be found", |blog| { let recents = Post::get_recents_for_blog(&*conn, &blog, 5); + let articles = Post::get_for_blog(&*conn, &blog); let authors = &blog.list_authors(&*conn); Template::render("blogs/details", json!({ @@ -31,7 +32,8 @@ fn details(name: String, conn: DbConn, user: Option) -> Template { "is_author": user.map(|x| x.is_author_in(&*conn, blog.clone())), "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), "authors": authors.into_iter().map(|u| u.to_json(&*conn)).collect::>(), - "n_authors": authors.len() + "n_authors": authors.len(), + "n_articles": articles.len() })) }) } diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index 96d805c1..37799663 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -19,7 +19,10 @@ {{ name }}{% if not loop.last %},{% endif %} {% endfor %}

- +

+ {{ "{{ count }} articles in this blog" | _n(singular="One article in this blog", count = n_articles) }} +

+

{{ "Latest articles" | _ }}

{% if recents | length < 1 %} From 4b84fb7ed0fb1d151a04769031a5d096b8ba03af Mon Sep 17 00:00:00 2001 From: "Kevin \"Ilphrin\" Pellet" Date: Sat, 21 Jul 2018 20:59:47 +0200 Subject: [PATCH 047/103] Add translation string for article count on blog --- po/plume.pot | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/po/plume.pot b/po/plume.pot index 0fdd5303..c61456af 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -325,3 +325,8 @@ msgstr "" msgid "Optional" msgstr "" + +msgid "One article in this blog" +msgid_plural "{{ count }} articles in this blog" +msgstr[0] "" +msgstr[1] "" From 89713526e2401b4210aba537a0e72966a3688ca0 Mon Sep 17 00:00:00 2001 From: "Kevin \"Ilphrin\" Pellet" Date: Sun, 22 Jul 2018 01:42:27 +0200 Subject: [PATCH 048/103] Add a link to the blog on article cards Fixes #134 --- plume-models/src/posts.rs | 7 +++++-- templates/macros.html.tera | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 5a04b9f8..22f02442 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -181,11 +181,14 @@ impl Post { } pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let blog = self.get_blog(conn); json!({ "post": self, "author": self.get_authors(conn)[0].to_json(conn), - "url": format!("/~/{}/{}/", self.get_blog(conn).actor_id, self.slug), - "date": self.creation_date.timestamp() + "url": format!("/~/{}/{}/", blog.actor_id, self.slug), + "date": self.creation_date.timestamp(), + "blog": blog, + "blog_url": format!("/~/{}", blog.actor_id) }) } diff --git a/templates/macros.html.tera b/templates/macros.html.tera index d474a6be..62d90dc3 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -18,6 +18,7 @@ link_4="") }} ⋅ {{ article.date | date(format="%B %e") }} + ⋅ {{article.blog.title}}

{% endmacro post_card %} From 04dac6f87fd2f0dfdd57155c3b144ceaad890928 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 25 Jul 2018 14:29:34 +0200 Subject: [PATCH 049/103] Add pagination links --- po/de.po | 6 ++++++ po/en.po | 5 +++++ po/fr.po | 6 ++++++ po/pl.po | 7 +++++++ src/routes/blogs.rs | 4 +++- src/routes/mod.rs | 9 +++++++++ templates/blogs/details.html.tera | 1 + templates/macros.html.tera | 10 ++++++++++ 8 files changed, 47 insertions(+), 1 deletion(-) diff --git a/po/de.po b/po/de.po index 207df2c7..2d442bad 100644 --- a/po/de.po +++ b/po/de.po @@ -339,3 +339,9 @@ msgstr "" msgid "Optional" msgstr "" + +#, fuzzy +msgid "One article in this blog" +msgid_plural "{{ count }} articles in this blog" +msgstr[0] "Du bist kein Autor in diesem Blog." +msgstr[1] "Du bist kein Autor in diesem Blog." diff --git a/po/en.po b/po/en.po index 40764f17..3da7d51e 100644 --- a/po/en.po +++ b/po/en.po @@ -332,3 +332,8 @@ msgstr "" msgid "Optional" msgstr "" + +msgid "One article in this blog" +msgid_plural "{{ count }} articles in this blog" +msgstr[0] "" +msgstr[1] "" diff --git a/po/fr.po b/po/fr.po index 07e1fe9b..c58c2ae3 100644 --- a/po/fr.po +++ b/po/fr.po @@ -335,3 +335,9 @@ msgstr "" msgid "Optional" msgstr "Optionnel" + +#, fuzzy +msgid "One article in this blog" +msgid_plural "{{ count }} articles in this blog" +msgstr[0] "{{ count }} aut⋅eur⋅rice dans ce blog : " +msgstr[1] "{{ count }} aut⋅eur⋅rice⋅s dans ce blog : " diff --git a/po/pl.po b/po/pl.po index 69163662..086f0fa4 100644 --- a/po/pl.po +++ b/po/pl.po @@ -343,5 +343,12 @@ msgstr "" msgid "Optional" msgstr "Nieobowiązkowe" +#, fuzzy +msgid "One article in this blog" +msgid_plural "{{ count }} articles in this blog" +msgstr[0] "Ten blog ma jednego autora: " +msgstr[1] "Ten blog ma {{ count }} autorów: " +msgstr[2] "Ten blog ma {{ count }} autorów: " + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index d725a525..39df22a0 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -34,7 +34,9 @@ fn paginated_details(name: String, conn: DbConn, user: Option, page: Page) "posts": posts.into_iter().map(|p| p.to_json(&*conn)).collect::>(), "authors": authors.into_iter().map(|u| u.to_json(&*conn)).collect::>(), "n_authors": authors.len(), - "n_articles": articles.len() + "n_articles": articles.len(), + "page": page.page, + "n_pages": Page::total(articles.len() as i32) })) }) } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 8bfd4cf0..c46fc2f2 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -61,6 +61,15 @@ impl Page { } } + /// Computes the total number of pages needed to display n_items + pub fn total(n_items: i32) -> i32 { + if n_items % ITEMS_PER_PAGE == 0 { + n_items / ITEMS_PER_PAGE + } else { + (n_items / ITEMS_PER_PAGE) + 1 + } + } + pub fn limits(&self) -> (i32, i32) { ((self.page - 1) * ITEMS_PER_PAGE, self.page * ITEMS_PER_PAGE) } diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index dd23af4d..9c484f60 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -36,5 +36,6 @@ {{ macros::post_card(article=article) }} {% endfor %} + {{ macros::paginate(page=page, total=n_pages) }} {% endblock content %} diff --git a/templates/macros.html.tera b/templates/macros.html.tera index d474a6be..e6b04564 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -35,3 +35,13 @@ {% endif %} {% endmacro input %} +{% macro paginate(page, total, previous="Previous page", next="Next page") %} + +{% endmacro %} From 740393bc18cbedb15baf43f48d8f3f0f2eb5ab9f Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 25 Jul 2018 14:41:48 +0200 Subject: [PATCH 050/103] Better style for pagination links --- po/plume.pot | 6 ++++++ static/main.css | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/po/plume.pot b/po/plume.pot index c61456af..197db640 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -330,3 +330,9 @@ msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" msgstr[0] "" msgstr[1] "" + +msgid "Previous page" +msgstr "" + +msgid "Next page" +msgstr "" diff --git a/static/main.css b/static/main.css index d523d8cd..985081ea 100644 --- a/static/main.css +++ b/static/main.css @@ -540,3 +540,14 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } min-height: 80%; } } + +/*== Pagination ==*/ +.pagination { + display: flex; + width: 100%; + justify-content: space-evenly; +} + +.pagination > * { + padding: 2em; +} From 18125ab398e5660e194fe52a18190991aca9369e Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 25 Jul 2018 15:20:09 +0200 Subject: [PATCH 051/103] Paginate the homepage --- plume-models/src/posts.rs | 12 ++++++++++++ po/de.po | 6 ++++++ po/en.po | 6 ++++++ po/fr.po | 6 ++++++ po/pl.po | 6 ++++++ src/main.rs | 1 + src/routes/instance.rs | 16 ++++++++++++---- static/main.css | 1 - templates/instance/index.html.tera | 1 + 9 files changed, 50 insertions(+), 5 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 2e7bc1c2..2601a752 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -65,6 +65,10 @@ impl Post { .len() } + pub fn count(conn: &PgConnection) -> i64 { + posts::table.count().get_result(conn).expect("Couldn't count posts") + } + pub fn get_recents(conn: &PgConnection, limit: i64) -> Vec { posts::table.order(posts::creation_date.desc()) .limit(limit) @@ -106,6 +110,14 @@ impl Post { .expect("Error loading a page of posts for blog") } + pub fn get_recents_page(conn: &PgConnection, (min, max): (i32, i32)) -> Vec { + posts::table.order(posts::creation_date.desc()) + .offset(min.into()) + .limit((max - min).into()) + .load::(conn) + .expect("Error loading recent posts page") + } + pub fn get_authors(&self, conn: &PgConnection) -> Vec { use schema::users; use schema::post_authors; diff --git a/po/de.po b/po/de.po index 2d442bad..b45843e9 100644 --- a/po/de.po +++ b/po/de.po @@ -345,3 +345,9 @@ msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" msgstr[0] "Du bist kein Autor in diesem Blog." msgstr[1] "Du bist kein Autor in diesem Blog." + +msgid "Previous page" +msgstr "" + +msgid "Next page" +msgstr "" diff --git a/po/en.po b/po/en.po index 3da7d51e..61639615 100644 --- a/po/en.po +++ b/po/en.po @@ -337,3 +337,9 @@ msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" msgstr[0] "" msgstr[1] "" + +msgid "Previous page" +msgstr "" + +msgid "Next page" +msgstr "" diff --git a/po/fr.po b/po/fr.po index c58c2ae3..a222daae 100644 --- a/po/fr.po +++ b/po/fr.po @@ -341,3 +341,9 @@ msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" msgstr[0] "{{ count }} aut⋅eur⋅rice dans ce blog : " msgstr[1] "{{ count }} aut⋅eur⋅rice⋅s dans ce blog : " + +msgid "Previous page" +msgstr "" + +msgid "Next page" +msgstr "" diff --git a/po/pl.po b/po/pl.po index 086f0fa4..775c04e8 100644 --- a/po/pl.po +++ b/po/pl.po @@ -350,5 +350,11 @@ msgstr[0] "Ten blog ma jednego autora: " msgstr[1] "Ten blog ma {{ count }} autorów: " msgstr[2] "Ten blog ma {{ count }} autorów: " +msgid "Previous page" +msgstr "" + +msgid "Next page" +msgstr "" + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/src/main.rs b/src/main.rs index c414e503..9fe76452 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,7 @@ fn main() { routes::comments::create, + routes::instance::paginated_index, routes::instance::index, routes::instance::shared_inbox, routes::instance::nodeinfo, diff --git a/src/routes/instance.rs b/src/routes/instance.rs index 09304dac..475d69cf 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -10,17 +10,20 @@ use plume_models::{ instance::* }; use inbox::Inbox; +use routes::Page; -#[get("/")] -fn index(conn: DbConn, user: Option) -> Template { +#[get("/?")] +fn paginated_index(conn: DbConn, user: Option, page: Page) -> Template { match Instance::get_local(&*conn) { Some(inst) => { - let recents = Post::get_recents(&*conn, 6); + let recents = Post::get_recents_page(&*conn, page.limits()); Template::render("instance/index", json!({ "instance": inst, "account": user, - "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>() + "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), + "page": page.page, + "n_pages": Page::total(Post::count(&*conn) as i32) })) } None => { @@ -31,6 +34,11 @@ fn index(conn: DbConn, user: Option) -> Template { } } +#[get("/")] +fn index(conn: DbConn, user: Option) -> Template { + paginated_index(conn, user, Page::first()) +} + #[post("/inbox", data = "")] fn shared_inbox(conn: DbConn, data: String) -> String { let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap(); diff --git a/static/main.css b/static/main.css index 985081ea..3e9c6da1 100644 --- a/static/main.css +++ b/static/main.css @@ -544,7 +544,6 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } /*== Pagination ==*/ .pagination { display: flex; - width: 100%; justify-content: space-evenly; } diff --git a/templates/instance/index.html.tera b/templates/instance/index.html.tera index 4ebec7a5..8fbc1b08 100644 --- a/templates/instance/index.html.tera +++ b/templates/instance/index.html.tera @@ -14,4 +14,5 @@ {{ macros::post_card(article=article) }} {% endfor %} + {{ macros::paginate(page=page, total=n_pages) }} {% endblock content %} From 4b0aba62f3a70d4f863ee81a3eb3114735502755 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 25 Jul 2018 15:33:54 +0200 Subject: [PATCH 052/103] Add pagination for notifications And correctly close an tag --- plume-models/src/notifications.rs | 9 +++++++++ src/main.rs | 1 + src/routes/notifications.rs | 16 ++++++++++++---- templates/notifications/index.html.tera | 4 +++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/plume-models/src/notifications.rs b/plume-models/src/notifications.rs index 10944359..07f0bdb2 100644 --- a/plume-models/src/notifications.rs +++ b/plume-models/src/notifications.rs @@ -35,4 +35,13 @@ impl Notification { .load::(conn) .expect("Couldn't load user notifications") } + + pub fn page_for_user(conn: &PgConnection, user: &User, (min, max): (i32, i32)) -> Vec { + notifications::table.filter(notifications::user_id.eq(user.id)) + .order_by(notifications::creation_date.desc()) + .offset(min.into()) + .limit((max - min).into()) + .load::(conn) + .expect("Couldn't load user notifications page") + } } diff --git a/src/main.rs b/src/main.rs index 9fe76452..a1c59e77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,6 +54,7 @@ fn main() { routes::likes::create, routes::likes::create_auth, + routes::notifications::paginated_notifications, routes::notifications::notifications, routes::notifications::notifications_auth, diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index 14cf6a74..de414944 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -3,13 +3,21 @@ use rocket_contrib::Template; use plume_common::utils; use plume_models::{db_conn::DbConn, notifications::Notification, users::User}; +use routes::Page; + +#[get("/notifications?")] +fn paginated_notifications(conn: DbConn, user: User, page: Page) -> Template { + Template::render("notifications/index", json!({ + "account": user, + "notifications": Notification::page_for_user(&*conn, &user, page.limits()), + "page": page.page, + "n_pages": Page::total(Notification::find_for_user(&*conn, &user).len() as i32) + })) +} #[get("/notifications")] fn notifications(conn: DbConn, user: User) -> Template { - Template::render("notifications/index", json!({ - "account": user, - "notifications": Notification::find_for_user(&*conn, &user) - })) + paginated_notifications(conn, user, Page::first()) } #[get("/notifications", rank = 2)] diff --git a/templates/notifications/index.html.tera b/templates/notifications/index.html.tera index 50752282..97034a50 100644 --- a/templates/notifications/index.html.tera +++ b/templates/notifications/index.html.tera @@ -1,4 +1,5 @@ {% extends "base" %} +{% import "macros" as macros %} {% block title %} {{ "Notifications" | _ }} @@ -9,11 +10,12 @@ + {{ macros::paginate(page=page, total=n_pages) }} {% endblock content %} From 4e07fdbd0555704e159b9698d5f0e017e991e572 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 25 Jul 2018 15:50:29 +0200 Subject: [PATCH 053/103] Paginate followers too --- plume-models/src/users.rs | 9 +++++++++ src/main.rs | 1 + src/routes/user.rs | 18 ++++++++++++++---- templates/users/followers.html.tera | 2 ++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index c43b15e8..ec74b1ca 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -275,6 +275,15 @@ impl User { users::table.filter(users::id.eq(any(follows))).load::(conn).unwrap() } + pub fn get_followers_page(&self, conn: &PgConnection, (min, max): (i32, i32)) -> Vec { + use schema::follows; + let follows = Follow::belonging_to(self).select(follows::follower_id); + users::table.filter(users::id.eq(any(follows))) + .offset(min.into()) + .limit((max - min).into()) + .load::(conn).unwrap() + } + pub fn get_following(&self, conn: &PgConnection) -> Vec { use schema::follows; let follows = follows::table.filter(follows::follower_id.eq(self.id)).select(follows::following_id); diff --git a/src/main.rs b/src/main.rs index a1c59e77..ceffc6e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,6 +79,7 @@ fn main() { routes::user::details, routes::user::dashboard, routes::user::dashboard_auth, + routes::user::followers_paginated, routes::user::followers, routes::user::edit, routes::user::edit_auth, diff --git a/src/routes/user.rs b/src/routes/user.rs index 6b131304..67fb0215 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -24,6 +24,7 @@ use plume_models::{ users::* }; use inbox::Inbox; +use routes::Page; #[get("/me")] fn me(user: Option) -> Result> { @@ -94,24 +95,33 @@ fn follow_auth(name: String) -> Flash { utils::requires_login("You need to be logged in order to follow someone", uri!(follow: name = name)) } -#[get("/@//followers", rank = 2)] -fn followers(name: String, conn: DbConn, account: Option) -> Template { +#[get("/@//followers?")] +fn followers_paginated(name: String, conn: DbConn, account: Option, page: Page) -> Template { may_fail!(account, User::find_by_fqn(&*conn, name.clone()), "Couldn't find requested user", |user| { let user_id = user.id.clone(); + let followers_count = user.get_followers(&*conn).len(); Template::render("users/followers", json!({ "user": user.to_json(&*conn), "instance_url": user.get_instance(&*conn).public_domain, "is_remote": user.instance_id != Instance::local_id(&*conn), "follows": account.clone().map(|x| x.is_following(&*conn, user.id)).unwrap_or(false), - "followers": user.get_followers(&*conn).into_iter().map(|f| f.to_json(&*conn)).collect::>(), + "followers": user.get_followers_page(&*conn, page.limits()).into_iter().map(|f| f.to_json(&*conn)).collect::>(), "account": account, "is_self": account.map(|a| a.id == user_id).unwrap_or(false), - "n_followers": user.get_followers(&*conn).len() + "n_followers": followers_count, + "page": page.page, + "n_pages": Page::total(followers_count as i32) })) }) } +#[get("/@//followers", rank = 2)] +fn followers(name: String, conn: DbConn, account: Option) -> Template { + followers_paginated(name, conn, account, Page::first()) +} + + #[get("/@/", rank = 1)] fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let user = User::find_local(&*conn, name).unwrap(); diff --git a/templates/users/followers.html.tera b/templates/users/followers.html.tera index e748116e..8757857a 100644 --- a/templates/users/followers.html.tera +++ b/templates/users/followers.html.tera @@ -1,4 +1,5 @@ {% extends "base" %} +{% import "macros" as macros %} {% block title %} {% if user.display_name %} @@ -27,4 +28,5 @@ {% endfor %} + {{ macros::paginate(page=page, total=n_pages) }} {% endblock content %} From 44172b67d5eb1a7fd5093b0e9193a99586449867 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 25 Jul 2018 18:18:41 +0200 Subject: [PATCH 054/103] Add padding for responses in comments, to let threads appear Fixes #144 --- plume-models/src/comments.rs | 6 +++++- src/routes/comments.rs | 5 +++-- src/routes/posts.rs | 9 +++++++-- static/main.css | 9 +++++++-- templates/macros.html.tera | 13 +++++++++++++ templates/posts/details.html.tera | 16 ++-------------- 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 260118eb..f1e4b454 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -69,13 +69,17 @@ impl Comment { .len() } - pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + pub fn to_json(&self, conn: &PgConnection, others: &Vec) -> serde_json::Value { let mut json = serde_json::to_value(self).unwrap(); json["author"] = self.get_author(conn).to_json(conn); let mentions = Mention::list_for_comment(conn, self.id).into_iter() .map(|m| m.get_mentioned(conn).map(|u| u.get_fqn(conn)).unwrap_or(String::new())) .collect::>(); json["mentions"] = serde_json::to_value(mentions).unwrap(); + json["responses"] = json!(others.into_iter() + .filter(|c| c.in_response_to_id.map(|id| id == self.id).unwrap_or(false)) + .map(|c| c.to_json(conn, others)) + .collect::>()); json } diff --git a/src/routes/comments.rs b/src/routes/comments.rs index d08998ed..f25489ff 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -48,19 +48,20 @@ fn create(blog_name: String, slug: String, data: LenientForm, us .map_err(|errors| { // TODO: de-duplicate this code let comments = Comment::list_by_post(&*conn, post.id); + let comms = comments.clone(); Template::render("posts/details", json!({ "author": post.get_authors(&*conn)[0].to_json(&*conn), "post": post, "blog": blog, - "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(), + "comments": &comments.into_iter().map(|c| c.to_json(&*conn, &comms)).collect::>(), "n_likes": post.get_likes(&*conn).len(), "has_liked": user.has_liked(&*conn, &post), "n_reshares": post.get_reshares(&*conn).len(), "has_reshared": user.has_reshared(&*conn, &post), "account": user, "date": &post.creation_date.timestamp(), - "previous": form.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn)), + "previous": form.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn, &vec![])), "user_fqn": user.get_fqn(&*conn), "errors": errors })) diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 95d9886b..d7787bf6 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -36,19 +36,24 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option may_fail!(user, Blog::find_by_fqn(&*conn, blog), "Couldn't find this blog", |blog| { may_fail!(user, Post::find_by_slug(&*conn, slug, blog.id), "Couldn't find this post", |post| { let comments = Comment::list_by_post(&*conn, post.id); + let comms = comments.clone(); Template::render("posts/details", json!({ "author": post.get_authors(&*conn)[0].to_json(&*conn), "post": post, "blog": blog, - "comments": comments.into_iter().map(|c| c.to_json(&*conn)).collect::>(), + "comments": &comments.into_iter().filter_map(|c| if c.in_response_to_id.is_none() { + Some(c.to_json(&*conn, &comms)) + } else { + None + }).collect::>(), "n_likes": post.get_likes(&*conn).len(), "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), "n_reshares": post.get_reshares(&*conn).len(), "has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false), "account": user, "date": &post.creation_date.timestamp(), - "previous": query.and_then(|q| q.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn))), + "previous": query.and_then(|q| q.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn, &vec![]))), "user_fqn": user.map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()) })) }) diff --git a/static/main.css b/static/main.css index 3e9c6da1..c946fde9 100644 --- a/static/main.css +++ b/static/main.css @@ -281,16 +281,21 @@ main .article-meta .comments > a.button { margin-bottom: 1em; } main .article-meta .comments .list { display: grid; margin: 0; + padding: 0 20%; + background: #ECECEC; } /* ~ Comment ~ */ .comments .list .comment { - background: #ECECEC; - padding: 2em 20%; + padding: 2em; font-size: 1em; } +.comments .list > .comment { + border: none; +} + .comments .list .comment .author { display: flex; flex-direction: row; diff --git a/templates/macros.html.tera b/templates/macros.html.tera index e6b04564..9315ca10 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -45,3 +45,16 @@ {% endif %} {% endmacro %} +{% macro comment(comm) %} +
+ + {{ comm.author.display_name | default(value=comm.author.username) }} + @{{ comm.author.username }} + +
{{ comm.content | safe }}
+ {{ "Respond" | _ }} + {% for res in comm.responses %} + {{ self::comment(comm=res) }} + {% endfor %} +
+{% endmacro %} diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index 6952b1ab..cc893adc 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -1,4 +1,5 @@ {% extends "base" %} +{% import "macros" as macros %} {% block title %} {{ post.title }} @@ -78,20 +79,7 @@
{% for comment in comments %} - {% if comment.author.display_name %} - {% set comment_author_name = comment.author.display_name %} - {% else %} - {% set comment_author_name = comment.author.username %} - {% endif %} - - + {{ macros::comment(comm=comment) }} {% endfor %}
From 390d694cac26ec7eb673424ae731706b0c32568a Mon Sep 17 00:00:00 2001 From: "Kevin \"Ilphrin\" Pellet" Date: Wed, 25 Jul 2018 21:21:47 +0200 Subject: [PATCH 055/103] Fix styling and field values --- templates/macros.html.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 62d90dc3..eadc13d2 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -18,7 +18,7 @@ link_4="") }} ⋅ {{ article.date | date(format="%B %e") }} - ⋅ {{article.blog.title}} + ⋅ {{ article.blog.title }}

{% endmacro post_card %} From aaae786fb73687dfccbd91e904ccdd55ac3fed3e Mon Sep 17 00:00:00 2001 From: "Kevin \"Ilphrin\" Pellet" Date: Wed, 25 Jul 2018 21:22:42 +0200 Subject: [PATCH 056/103] Implement new function to Blog for the view Added Blog::to_json and Blog::get_fqn --- plume-models/src/blogs.rs | 15 +++++++++++++++ plume-models/src/posts.rs | 3 +-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 7123d36a..59991be6 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -248,6 +248,21 @@ impl Blog { } }) } + + pub fn get_fqn(&self, conn: &PgConnection) -> String { + if self.instance_id == Instance::local_id(conn) { + self.actor_id.clone() + } else { + format!("{}@{}", self.actor_id, self.get_instance(conn).public_domain) + } + } + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let mut json = serde_json::to_value(self).unwrap(); + let formatted = serde_json::Value::String(format!("/~/{}",self.get_fqn(conn))); + json["fqn"] = formatted; + json + } } impl IntoId for Blog { diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 22f02442..3c0f1eb1 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -187,8 +187,7 @@ impl Post { "author": self.get_authors(conn)[0].to_json(conn), "url": format!("/~/{}/{}/", blog.actor_id, self.slug), "date": self.creation_date.timestamp(), - "blog": blog, - "blog_url": format!("/~/{}", blog.actor_id) + "blog": blog.to_json(conn) }) } From c87d490664a488259f36a9deab7dc4c86ec13374 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 15:46:10 +0200 Subject: [PATCH 057/103] Refactor notifications --- .../down.sql | 8 +++ .../up.sql | 8 +++ plume-models/src/comments.rs | 6 +- plume-models/src/follows.rs | 7 +- plume-models/src/likes.rs | 8 +-- plume-models/src/mentions.rs | 17 ++--- plume-models/src/notifications.rs | 70 ++++++++++++++++--- plume-models/src/reshares.rs | 14 ++-- plume-models/src/schema.rs | 6 +- po/plume.pot | 15 ++++ src/routes/notifications.rs | 2 +- templates/notifications/index.html.tera | 31 +++++++- 12 files changed, 146 insertions(+), 46 deletions(-) create mode 100644 migrations/2018-07-25-165754_refactor_notifications/down.sql create mode 100644 migrations/2018-07-25-165754_refactor_notifications/up.sql diff --git a/migrations/2018-07-25-165754_refactor_notifications/down.sql b/migrations/2018-07-25-165754_refactor_notifications/down.sql new file mode 100644 index 00000000..53830f4a --- /dev/null +++ b/migrations/2018-07-25-165754_refactor_notifications/down.sql @@ -0,0 +1,8 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE notifications ADD COLUMN title VARCHAR NOT NULL; +ALTER TABLE notifications ADD COLUMN content TEXT; +ALTER TABLE notifications ADD COLUMN link VARCHAR; +ALTER TABLE notifications ADD COLUMN data VARCHAR; + +ALTER TABLE notifications DROP COLUMN kind; +ALTER TABLE notifications DROP COLUMN object_id; diff --git a/migrations/2018-07-25-165754_refactor_notifications/up.sql b/migrations/2018-07-25-165754_refactor_notifications/up.sql new file mode 100644 index 00000000..e3ab6641 --- /dev/null +++ b/migrations/2018-07-25-165754_refactor_notifications/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here +ALTER TABLE notifications DROP COLUMN title; +ALTER TABLE notifications DROP COLUMN content; +ALTER TABLE notifications DROP COLUMN link; +ALTER TABLE notifications DROP COLUMN data; + +ALTER TABLE notifications ADD COLUMN kind VARCHAR NOT NULL DEFAULT 'unknown'; +ALTER TABLE notifications ADD COLUMN object_id INTEGER NOT NULL DEFAULT 0; diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index f1e4b454..39926d5c 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -123,10 +123,8 @@ impl Notify for Comment { fn notify(&self, conn: &PgConnection) { for author in self.get_post(conn).get_authors(conn) { Notification::insert(conn, NewNotification { - title: "{{ data }} commented your article".to_string(), - data: Some(self.get_author(conn).display_name.clone()), - content: Some(self.get_post(conn).title), - link: self.ap_url.clone(), + kind: notification_kind::COMMENT.to_string(), + object_id: self.id, user_id: author.id }); } diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index 05c50a38..57e26fa4 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -70,12 +70,9 @@ impl FromActivity for Follow { impl Notify for Follow { fn notify(&self, conn: &PgConnection) { - let follower = User::get(conn, self.follower_id).unwrap(); Notification::insert(conn, NewNotification { - title: "{{ data }} started following you".to_string(), - data: Some(follower.display_name.clone()), - content: None, - link: Some(follower.ap_url), + kind: notification_kind::FOLLOW.to_string(), + object_id: self.id, user_id: self.following_id }); } diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index e8e084d2..651b4d8d 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -89,15 +89,11 @@ impl FromActivity for Like { impl Notify for Like { fn notify(&self, conn: &PgConnection) { - let liker = User::get(conn, self.user_id).unwrap(); let post = Post::get(conn, self.post_id).unwrap(); for author in post.get_authors(conn) { - let post = post.clone(); Notification::insert(conn, NewNotification { - title: "{{ data }} liked your article".to_string(), - data: Some(liker.display_name.clone()), - content: Some(post.title), - link: Some(post.ap_url), + kind: notification_kind::LIKE.to_string(), + object_id: self.id, user_id: author.id }); } diff --git a/plume-models/src/mentions.rs b/plume-models/src/mentions.rs index 0d2e586c..c338a5cb 100644 --- a/plume-models/src/mentions.rs +++ b/plume-models/src/mentions.rs @@ -46,6 +46,13 @@ impl Mention { self.comment_id.and_then(|id| Comment::get(conn, id)) } + pub fn get_user(&self, conn: &PgConnection) -> Option { + match self.get_post(conn) { + Some(p) => p.get_authors(conn).into_iter().next(), + None => self.get_comment(conn).map(|c| c.get_author(conn)) + } + } + pub fn build_activity(conn: &PgConnection, ment: String) -> link::Mention { let user = User::find_by_fqn(conn, ment.clone()); let mut mention = link::Mention::default(); @@ -94,16 +101,10 @@ impl Mention { impl Notify for Mention { fn notify(&self, conn: &PgConnection) { - let author = self.get_comment(conn) - .map(|c| c.get_author(conn).display_name.clone()) - .unwrap_or_else(|| self.get_post(conn).unwrap().get_authors(conn)[0].display_name.clone()); - self.get_mentioned(conn).map(|m| { Notification::insert(conn, NewNotification { - title: "{{ data }} mentioned you.".to_string(), - data: Some(author), - content: None, - link: Some(self.get_post(conn).map(|p| p.ap_url).unwrap_or_else(|| self.get_comment(conn).unwrap().ap_url.unwrap_or(String::new()))), + kind: notification_kind::MENTION.to_string(), + object_id: self.id, user_id: m.id }); }); diff --git a/plume-models/src/notifications.rs b/plume-models/src/notifications.rs index 07f0bdb2..33b06c1a 100644 --- a/plume-models/src/notifications.rs +++ b/plume-models/src/notifications.rs @@ -1,28 +1,39 @@ use chrono::NaiveDateTime; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods}; +use serde_json; +use comments::Comment; +use follows::Follow; +use likes::Like; +use mentions::Mention; +use posts::Post; +use reshares::Reshare; use users::User; use schema::notifications; +pub mod notification_kind { + pub const COMMENT: &'static str = "COMMENT"; + pub const FOLLOW: &'static str = "FOLLOW"; + pub const LIKE: &'static str = "LIKE"; + pub const MENTION: &'static str = "MENTION"; + pub const RESHARE: &'static str = "RESHARE"; +} + #[derive(Queryable, Identifiable, Serialize)] pub struct Notification { pub id: i32, - pub title: String, - pub content: Option, - pub link: Option, pub user_id: i32, pub creation_date: NaiveDateTime, - pub data: Option + pub kind: String, + pub object_id: i32 } #[derive(Insertable)] #[table_name = "notifications"] pub struct NewNotification { - pub title: String, - pub content: Option, - pub link: Option, pub user_id: i32, - pub data: Option + pub kind: String, + pub object_id: i32 } impl Notification { @@ -44,4 +55,47 @@ impl Notification { .load::(conn) .expect("Couldn't load user notifications page") } + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let mut json = json!(self); + json["object"] = json!(match self.kind.as_ref() { + notification_kind::COMMENT => Comment::get(conn, self.object_id).map(|comment| + json!({ + "post": comment.get_post(conn).to_json(conn), + "user": comment.get_author(conn).to_json(conn), + "id": comment.id + }) + ), + notification_kind::FOLLOW => Follow::get(conn, self.object_id).map(|follow| + json!({ + "follower": User::get(conn, follow.follower_id).map(|u| u.to_json(conn)) + }) + ), + notification_kind::LIKE => Like::get(conn, self.object_id).map(|like| + json!({ + "post": Post::get(conn, like.post_id).map(|p| p.to_json(conn)), + "user": User::get(conn, like.user_id).map(|u| u.to_json(conn)) + }) + ), + notification_kind::MENTION => Mention::get(conn, self.object_id).map(|mention| + json!({ + "user": mention.get_user(conn).map(|u| u.to_json(conn)), + "url": mention.get_post(conn).map(|p| p.to_json(conn)["url"].clone()) + .unwrap_or_else(|| { + let comment = mention.get_comment(conn).expect("No comment nor post for mention"); + let post = comment.get_post(conn).to_json(conn); + json!(format!("{}#comment-{}", post["url"].as_str().unwrap(), comment.id)) + }) + }) + ), + notification_kind::RESHARE => Reshare::get(conn, self.object_id).map(|reshare| + json!({ + "post": reshare.get_post(conn).map(|p| p.to_json(conn)), + "user": reshare.get_user(conn).map(|u| u.to_json(conn)) + }) + ), + _ => Some(json!({})) + }); + json + } } diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 335baf84..2acb0a44 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -5,7 +5,7 @@ use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use plume_common::activity_pub::{Id, IntoId, inbox::{FromActivity, Notify, Deletable}, PUBLIC_VISIBILTY}; use notifications::*; use posts::Post; -use users::User; +use users::User; use schema::reshares; #[derive(Serialize, Deserialize, Queryable, Identifiable)] @@ -55,6 +55,10 @@ impl Reshare { Post::get(conn, self.post_id) } + pub fn get_user(&self, conn: &PgConnection) -> Option { + User::get(conn, self.user_id) + } + pub fn delete(&self, conn: &PgConnection) -> Undo { diesel::delete(self).execute(conn).unwrap(); @@ -96,15 +100,11 @@ impl FromActivity for Reshare { impl Notify for Reshare { fn notify(&self, conn: &PgConnection) { - let actor = User::get(conn, self.user_id).unwrap(); let post = self.get_post(conn).unwrap(); for author in post.get_authors(conn) { - let post = post.clone(); Notification::insert(conn, NewNotification { - title: "{{ data }} reshared your article".to_string(), - data: Some(actor.display_name.clone()), - content: Some(post.title), - link: Some(post.ap_url), + kind: notification_kind::RESHARE.to_string(), + object_id: self.id, user_id: author.id }); } diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index 42184146..c19136d0 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -79,12 +79,10 @@ table! { table! { notifications (id) { id -> Int4, - title -> Varchar, - content -> Nullable, - link -> Nullable, user_id -> Int4, creation_date -> Timestamp, - data -> Nullable, + kind -> Varchar, + object_id -> Int4, } } diff --git a/po/plume.pot b/po/plume.pot index 197db640..31806f63 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -336,3 +336,18 @@ msgstr "" msgid "Next page" msgstr "" + +msgid "{{ user }} mentioned you." +msgstr "" + +msgid "{{ user }} commented your article." +msgstr "" + +msgid "{{ user }} is now following you." +msgstr "" + +msgid "{{ user }} liked your article." +msgstr "" + +msgstr "{{ user }} reshare your article." +msgid "" diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index de414944..64def8da 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -9,7 +9,7 @@ use routes::Page; fn paginated_notifications(conn: DbConn, user: User, page: Page) -> Template { Template::render("notifications/index", json!({ "account": user, - "notifications": Notification::page_for_user(&*conn, &user, page.limits()), + "notifications": Notification::page_for_user(&*conn, &user, page.limits()).into_iter().map(|n| n.to_json(&*conn)).collect::>(), "page": page.page, "n_pages": Page::total(Notification::find_for_user(&*conn, &user).len() as i32) })) diff --git a/templates/notifications/index.html.tera b/templates/notifications/index.html.tera index 97034a50..8c4f194e 100644 --- a/templates/notifications/index.html.tera +++ b/templates/notifications/index.html.tera @@ -10,9 +10,34 @@
{% for notification in notifications %}
-

{{ notification.title | _(data=notification.data) }}

- {% if notification.content %} -

{{ notification.content }}

+ {% if notification.kind == "COMMENT" %} +

+ {{ "{{ user }} commented your article." | _(user=notification.object.user.display_name) }} +

+

{{ notification.object.post.title }}

+ + {% elif notification.kind == "FOLLOW" %} +

+ {{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name) }} +

+ + {% elif notification.kind == "LIKE" %} +

+ {{ "{{ user }} liked your article." | _(user=notification.object.user.display_name) }} +

+

{{ notification.object.post.title }}

+ + {% elif notification.kind == "MENTION" %} +

+ {{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name) }} +

+ + {% elif notification.kind == "RESHARE" %} +

+ {{ "{{ user }} reshare your article." | _(user=notification.object.user.display_name) }} +

+

{{ notification.object.post.title }}

+ {% endif %}
{% endfor %} From 7a6c01fec503f0bd72c3643c37a8023cb93b226d Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 16:09:36 +0200 Subject: [PATCH 058/103] Add icons to notifications And some space between each of them Fix #139 --- po/de.po | 20 ++++++++++ po/en.po | 15 +++++++ po/fr.po | 20 ++++++++++ po/pl.po | 20 ++++++++++ po/plume.pot | 4 +- static/main.css | 21 +++++++++- templates/notifications/index.html.tera | 53 ++++++++++++++++--------- 7 files changed, 131 insertions(+), 22 deletions(-) diff --git a/po/de.po b/po/de.po index b45843e9..89940da7 100644 --- a/po/de.po +++ b/po/de.po @@ -351,3 +351,23 @@ msgstr "" msgid "Next page" msgstr "" + +#, fuzzy +msgid "{{ user }} mentioned you." +msgstr "{{ data }} hat dich erwähnt." + +#, fuzzy +msgid "{{ user }} commented your article." +msgstr "{{ data }} hat deinen Artikel kommentiert" + +#, fuzzy +msgid "{{ user }} is now following you." +msgstr "{{ data }} folgt dir nun" + +#, fuzzy +msgid "{{ user }} liked your article." +msgstr "{{ data }} hat deinen Artikel geliked" + +#, fuzzy +msgid "{{ user }} reshared your article." +msgstr "{{ data }} hat deinen Artikel reshared" diff --git a/po/en.po b/po/en.po index 61639615..cc09dcf9 100644 --- a/po/en.po +++ b/po/en.po @@ -343,3 +343,18 @@ msgstr "" msgid "Next page" msgstr "" + +msgid "{{ user }} mentioned you." +msgstr "" + +msgid "{{ user }} commented your article." +msgstr "" + +msgid "{{ user }} is now following you." +msgstr "" + +msgid "{{ user }} liked your article." +msgstr "" + +msgid "{{ user }} reshared your article." +msgstr "" diff --git a/po/fr.po b/po/fr.po index a222daae..5c54aa49 100644 --- a/po/fr.po +++ b/po/fr.po @@ -347,3 +347,23 @@ msgstr "" msgid "Next page" msgstr "" + +#, fuzzy +msgid "{{ user }} mentioned you." +msgstr "{{ data }} vous a mentionné." + +#, fuzzy +msgid "{{ user }} commented your article." +msgstr "{{ data }} a commenté votre article" + +#, fuzzy +msgid "{{ user }} is now following you." +msgstr "{{ data }} vous suit" + +#, fuzzy +msgid "{{ user }} liked your article." +msgstr "{{ data }} a aimé votre article" + +#, fuzzy +msgid "{{ user }} reshared your article." +msgstr "{{ data }} a repartagé votre article" diff --git a/po/pl.po b/po/pl.po index 775c04e8..b3579b1b 100644 --- a/po/pl.po +++ b/po/pl.po @@ -356,5 +356,25 @@ msgstr "" msgid "Next page" msgstr "" +#, fuzzy +msgid "{{ user }} mentioned you." +msgstr "{{ data }} wspomniał o Tobie." + +#, fuzzy +msgid "{{ user }} commented your article." +msgstr "{{ data }} skomentował Twój artykuł" + +#, fuzzy +msgid "{{ user }} is now following you." +msgstr "{{ data }} zaczął Cię obserwować" + +#, fuzzy +msgid "{{ user }} liked your article." +msgstr "{{ data }} polubił Twój artykuł" + +#, fuzzy +msgid "{{ user }} reshared your article." +msgstr "{{ data }} udostępnił Twój artykuł" + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/po/plume.pot b/po/plume.pot index 31806f63..7d02ab95 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -349,5 +349,5 @@ msgstr "" msgid "{{ user }} liked your article." msgstr "" -msgstr "{{ user }} reshare your article." -msgid "" +msgid "{{ user }} reshared your article." +msgstr "" diff --git a/static/main.css b/static/main.css index c946fde9..35341678 100644 --- a/static/main.css +++ b/static/main.css @@ -468,7 +468,7 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } .list .card { /* TODO */ background: 0; - margin: 0; + margin: 2em 0; padding: 0; min-height: 0; } @@ -555,3 +555,22 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } .pagination > * { padding: 2em; } + +/*== Flex boxes ==*/ +.flex { + display: flex; + flex-direction: row; +} + +.flex .grow { + flex: 1; +} + +.left-icon { + align-self: center; + padding: 1em; + background: #DADADA; + border-radius: 50px; + margin: 1em; + margin-right: 2em; +} diff --git a/templates/notifications/index.html.tera b/templates/notifications/index.html.tera index 8c4f194e..859b0069 100644 --- a/templates/notifications/index.html.tera +++ b/templates/notifications/index.html.tera @@ -9,34 +9,49 @@

{{ "Notifications" | _ }}

{% for notification in notifications %} -
+
{% if notification.kind == "COMMENT" %} -

- {{ "{{ user }} commented your article." | _(user=notification.object.user.display_name) }} -

-

{{ notification.object.post.title }}

+ +
+

+ {{ "{{ user }} commented your article." | _(user=notification.object.user.display_name | escape) }} +

+

{{ notification.object.post.post.title }}

+
{% elif notification.kind == "FOLLOW" %} -

- {{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name) }} -

+ +
+

+ {{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name | escape) }} +

+
{% elif notification.kind == "LIKE" %} -

- {{ "{{ user }} liked your article." | _(user=notification.object.user.display_name) }} -

-

{{ notification.object.post.title }}

+ +
+

+ {{ "{{ user }} liked your article." | _(user=notification.object.user.display_name | escape) }} +

+

{{ notification.object.post.post.title }}

+
{% elif notification.kind == "MENTION" %} -

- {{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name) }} -

+ +
+

+ {{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name | escape) }} +

+
{% elif notification.kind == "RESHARE" %} -

- {{ "{{ user }} reshare your article." | _(user=notification.object.user.display_name) }} -

-

{{ notification.object.post.title }}

+ +
+

+ {{ "{{ user }} reshared your article." | _(user=notification.object.user.display_name | escape) }} +

+

{{ notification.object.post.post.title }}

+
{% endif %}
From 65e0d72a73bd0a4b02001b845c9b25653f39896f Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 16:14:01 +0200 Subject: [PATCH 059/103] Display date for notifications --- templates/notifications/index.html.tera | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/notifications/index.html.tera b/templates/notifications/index.html.tera index 859b0069..a6ba98d1 100644 --- a/templates/notifications/index.html.tera +++ b/templates/notifications/index.html.tera @@ -18,6 +18,7 @@

{{ notification.object.post.post.title }}

+

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

{% elif notification.kind == "FOLLOW" %} @@ -26,6 +27,7 @@ {{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name | escape) }} +

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

{% elif notification.kind == "LIKE" %} @@ -35,6 +37,7 @@

{{ notification.object.post.post.title }}

+

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

{% elif notification.kind == "MENTION" %} @@ -43,6 +46,7 @@ {{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name | escape) }} +

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

{% elif notification.kind == "RESHARE" %} @@ -52,6 +56,7 @@

{{ notification.object.post.post.title }}

+

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

{% endif %}
From 15477875da6b43643ef479f0576995c9285e2abd Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 16:19:21 +0200 Subject: [PATCH 060/103] Fix links for post from remote blogs --- plume-models/src/posts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index d090974e..947f7742 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -206,14 +206,14 @@ impl Post { json!({ "post": self, "author": self.get_authors(conn)[0].to_json(conn), - "url": format!("/~/{}/{}/", blog.actor_id, self.slug), + "url": format!("/~/{}/{}/", blog.get_fqn(conn), self.slug), "date": self.creation_date.timestamp(), "blog": blog.to_json(conn) }) } pub fn compute_id(&self, conn: &PgConnection) -> String { - ap_url(format!("{}/~/{}/{}/", BASE_URL.as_str(), self.get_blog(conn).actor_id, self.slug)) + ap_url(format!("{}/~/{}/{}/", BASE_URL.as_str(), self.get_blog(conn).get_fqn(conn), self.slug)) } } From 5980c7b299c1c594a09b2df0e9bea078397de7d2 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 16:36:19 +0200 Subject: [PATCH 061/103] Simplify the logic to fallback to the FQN when no display name is available --- plume-models/src/users.rs | 5 +++++ templates/blogs/details.html.tera | 7 +------ templates/macros.html.tera | 9 ++------- templates/notifications/index.html.tera | 10 +++++----- templates/posts/details.html.tera | 9 +-------- templates/users/followers.html.tera | 15 ++------------- templates/users/header.html.tera | 2 +- 7 files changed, 17 insertions(+), 40 deletions(-) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index ec74b1ca..e95babcc 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -370,6 +370,11 @@ impl User { pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { let mut json = serde_json::to_value(self).unwrap(); json["fqn"] = serde_json::Value::String(self.get_fqn(conn)); + json["name"] = if self.display_name.len() > 0 { + json!(self.display_name) + } else { + json!(self.get_fqn(conn)) + }; json } diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index 9c484f60..8a639f39 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -11,12 +11,7 @@

{{ "{{ count }} authors in this blog: " | _n(singular="One author in this blog: ", count = n_authors) }} {% for author in authors %} - {% if author.display_name %} - {% set name = author.display_name %} - {% else %} - {% set name = author.username %} - {% endif %} - {{ name }}{% if not loop.last %},{% endif %} + {{ author.name }}{% if not loop.last %},{% endif %} {% endfor %}

diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 48fbb110..50a192e4 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -1,9 +1,4 @@ {% macro post_card(article) %} - {% if article.author.display_name %} - {% set name = article.author.display_name %} - {% else %} - {% set name = article.author.username %} - {% endif %}

{{ article.post.title }}

- {{ comm.author.display_name | default(value=comm.author.username) }} + {{ comm.author.name }} @{{ comm.author.username }}
{{ comm.content | safe }}
diff --git a/templates/notifications/index.html.tera b/templates/notifications/index.html.tera index a6ba98d1..b608da1b 100644 --- a/templates/notifications/index.html.tera +++ b/templates/notifications/index.html.tera @@ -14,7 +14,7 @@

- {{ "{{ user }} commented your article." | _(user=notification.object.user.display_name | escape) }} + {{ "{{ user }} commented your article." | _(user=notification.object.user.name | escape) }}

{{ notification.object.post.post.title }}

@@ -24,7 +24,7 @@

- {{ "{{ user }} is now following you." | _(user=notification.object.follower.display_name | escape) }} + {{ "{{ user }} is now following you." | _(user=notification.object.follower.name | escape) }}

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

@@ -33,7 +33,7 @@

- {{ "{{ user }} liked your article." | _(user=notification.object.user.display_name | escape) }} + {{ "{{ user }} liked your article." | _(user=notification.object.user.name | escape) }}

{{ notification.object.post.post.title }}

@@ -43,7 +43,7 @@

- {{ "{{ user }} mentioned you." | _(user=notification.object.user.display_name | escape) }} + {{ "{{ user }} mentioned you." | _(user=notification.object.user.name | escape) }}

{{ notification.creation_date | date(format="%B %e, %H:%m") }}

@@ -52,7 +52,7 @@

- {{ "{{ user }} reshared your article." | _(user=notification.object.user.display_name | escape) }} + {{ "{{ user }} reshared your article." | _(user=notification.object.user.name | escape) }}

{{ notification.object.post.post.title }}

diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index cc893adc..0ac3f44f 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -12,18 +12,11 @@ {% block content %}

{{ post.title }}

{{ "Followers" | _ }}

{% for follower in followers %} - {% if follower.display_name %} - {% set follower_name = follower.display_name %} - {% else %} - {% set follower_name = follower.username %} - {% endif %}
-

{{ follower_name }} @{{ follower.fqn }}

+

{{ follower.name }} @{{ follower.fqn }}

{{ follower.summary | safe }}

{% endfor %} diff --git a/templates/users/header.html.tera b/templates/users/header.html.tera index 155081b0..8f66eb69 100644 --- a/templates/users/header.html.tera +++ b/templates/users/header.html.tera @@ -1,6 +1,6 @@

- {{ name }} + {{ user.name }} {% if user.is_admin %} {{ "Admin" | _ }} {% endif %} From 1e5ad2b086678a7da5a4aa7d585c21b1cd1ce880 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 17:32:52 +0200 Subject: [PATCH 062/103] Add a job pool --- Cargo.lock | 19 ++++++++++++++++--- Cargo.toml | 3 ++- src/main.rs | 3 +++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 760bc6c5..38ad4c91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1005,6 +1005,7 @@ dependencies = [ "validator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "validator_derive 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "webfinger 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "workerpool 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1348,7 +1349,7 @@ dependencies = [ "indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "pear 0.1.0 (git+http://github.com/SergioBenitez/Pear?rev=54667ae)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1523,8 +1524,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "state" @@ -2037,6 +2041,14 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "workerpool" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2220,7 +2232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum slug 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "797bcb4d24e91239a8615415814f4afb2d8ca400c472de3c73f803a5a7689e11" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" -"checksum smallvec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "312a7df010092e73d6bbaf141957e868d4f30efd2bfd9bb1028ad91abec58514" +"checksum smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8" "checksum state 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" "checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191" @@ -2280,5 +2292,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum workerpool 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f49756646617bde19ff95b370cfa5c0f7ead17a90c90d7cb62dc31dfaa8c625" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48" diff --git a/Cargo.toml b/Cargo.toml index 84d25c07..371879eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ serde_json = "1.0" validator = "0.7" validator_derive = "0.7" webfinger = "0.2" +workerpool = "1.1" [dependencies.diesel] features = ["postgres", "r2d2", "chrono"] @@ -41,8 +42,8 @@ git = "https://github.com/SergioBenitez/Rocket" rev = "df7111143e466c18d1f56377a8d9530a5a306aba" [dependencies.rocket_csrf] -git = "https://github.com/fdb-hiroshima/rocket_csrf" branch = "plume" +git = "https://github.com/fdb-hiroshima/rocket_csrf" [dependencies.rocket_i18n] git = "https://github.com/BaptisteGelez/rocket_i18n" diff --git a/src/main.rs b/src/main.rs index ceffc6e2..463c857a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,9 +24,11 @@ extern crate validator; #[macro_use] extern crate validator_derive; extern crate webfinger; +extern crate workerpool; use rocket_contrib::Template; use rocket_csrf::CsrfFairingBuilder; +use workerpool::{Pool, thunk::ThunkWorker}; mod inbox; mod setup; @@ -104,6 +106,7 @@ fn main() { routes::errors::server_error ]) .manage(pool) + .manage(Pool::>::new(4)) .attach(Template::custom(|engines| { rocket_i18n::tera(&mut engines.tera); })) From 58d158238de55b5bd82a21bbb1b5bcf0faaaf1fb Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 17:38:22 +0200 Subject: [PATCH 063/103] Use the worker queue to send new articles to other instances --- src/routes/posts.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/routes/posts.rs b/src/routes/posts.rs index d7787bf6..95016bbb 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -1,11 +1,12 @@ use activitypub::object::Article; use heck::KebabCase; -use rocket::request::LenientForm; +use rocket::{State, request::LenientForm}; use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; +use workerpool::{Pool, thunk::*}; use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest}; use plume_common::utils; @@ -110,7 +111,7 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> { } #[post("/~//new", data = "")] -fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn) -> Result { +fn create(blog_name: String, data: LenientForm, user: User, conn: DbConn, worker: State>>) -> Result { let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); @@ -154,7 +155,8 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D } let act = post.create_activity(&*conn); - broadcast(&user, act, user.get_followers(&*conn)); + let followers = user.get_followers(&*conn); + worker.execute(Thunk::of(move || broadcast(&user, act, followers))); Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug))) } From a9f95c91e2ed00421741a5f98454c5743e1577c0 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 17:51:41 +0200 Subject: [PATCH 064/103] Sent activities in other threads --- src/routes/comments.rs | 8 ++++++-- src/routes/likes.rs | 12 ++++++++---- src/routes/reshares.rs | 12 ++++++++---- src/routes/user.rs | 9 ++++++--- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/routes/comments.rs b/src/routes/comments.rs index f25489ff..a10c3cdd 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -1,10 +1,12 @@ use rocket::{ + State, request::LenientForm, response::Redirect }; use rocket_contrib::Template; use serde_json; use validator::Validate; +use workerpool::{Pool, thunk::*}; use plume_common::activity_pub::broadcast; use plume_models::{ @@ -25,7 +27,7 @@ struct NewCommentForm { } #[post("/~///comment", data = "")] -fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn) -> Result { +fn create(blog_name: String, slug: String, data: LenientForm, user: User, conn: DbConn, worker: State>>) -> Result { let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap(); let form = data.get(); @@ -41,7 +43,9 @@ fn create(blog_name: String, slug: String, data: LenientForm, us let instance = Instance::get_local(&*conn).unwrap(); instance.received(&*conn, serde_json::to_value(new_comment.clone()).expect("JSON serialization error")) .expect("We are not compatible with ourselve: local broadcast failed (new comment)"); - broadcast(&user, new_comment, user.get_followers(&*conn)); + let followers = user.get_followers(&*conn); + let user_clone = user.clone(); + worker.execute(Thunk::of(move || broadcast(&user_clone, new_comment, followers))); Redirect::to(format!("/~/{}/{}/#comment-{}", blog_name, slug, id)) }) diff --git a/src/routes/likes.rs b/src/routes/likes.rs index 8126871b..a3a2dd35 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -1,4 +1,5 @@ -use rocket::response::{Redirect, Flash}; +use rocket::{State, response::{Redirect, Flash}}; +use workerpool::{Pool, thunk::*}; use plume_common::activity_pub::{broadcast, inbox::Notify}; use plume_common::utils; @@ -11,7 +12,7 @@ use plume_models::{ }; #[post("/~///like")] -fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { +fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State>>) -> Redirect { let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap(); @@ -24,11 +25,14 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { like.update_ap_url(&*conn); like.notify(&*conn); - broadcast(&user, like.into_activity(&*conn), user.get_followers(&*conn)); + let followers = user.get_followers(&*conn); + let act = like.into_activity(&*conn); + worker.execute(Thunk::of(move || broadcast(&user, act, followers))); } else { let like = likes::Like::find_by_user_on_post(&*conn, user.id, post.id).unwrap(); let delete_act = like.delete(&*conn); - broadcast(&user, delete_act, user.get_followers(&*conn)); + let followers = user.get_followers(&*conn); + worker.execute(Thunk::of(move || broadcast(&user, delete_act, followers))); } Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)) diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index 67cf87a9..1ba673a5 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -1,4 +1,5 @@ -use rocket::response::{Redirect, Flash}; +use rocket::{State, response::{Redirect, Flash}}; +use workerpool::{Pool, thunk::*}; use plume_common::activity_pub::{broadcast, inbox::Notify}; use plume_common::utils; @@ -11,7 +12,7 @@ use plume_models::{ }; #[post("/~///reshare")] -fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { +fn create(blog: String, slug: String, user: User, conn: DbConn, worker: State>>) -> Redirect { let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap(); let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap(); @@ -24,11 +25,14 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { reshare.update_ap_url(&*conn); reshare.notify(&*conn); - broadcast(&user, reshare.into_activity(&*conn), user.get_followers(&*conn)); + let followers = user.get_followers(&*conn); + let act = reshare.into_activity(&*conn); + worker.execute(Thunk::of(move || broadcast(&user, act, followers))); } else { let reshare = Reshare::find_by_user_on_post(&*conn, user.id, post.id).unwrap(); let delete_act = reshare.delete(&*conn); - broadcast(&user, delete_act, user.get_followers(&*conn)); + let followers = user.get_followers(&*conn); + worker.execute(Thunk::of(move || broadcast(&user, delete_act, followers))); } Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)) diff --git a/src/routes/user.rs b/src/routes/user.rs index 67fb0215..cab8e6a0 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -2,12 +2,15 @@ use activitypub::{ activity::Follow, collection::OrderedCollection }; -use rocket::{request::LenientForm, +use rocket::{ + State, + request::LenientForm, response::{Redirect, Flash} }; use rocket_contrib::Template; use serde_json; use validator::{Validate, ValidationError}; +use workerpool::{Pool, thunk::*}; use plume_common::activity_pub::{ ActivityStream, broadcast, Id, IntoId, ApRequest, @@ -71,7 +74,7 @@ fn dashboard_auth() -> Flash { } #[get("/@//follow")] -fn follow(name: String, conn: DbConn, user: User) -> Redirect { +fn follow(name: String, conn: DbConn, user: User, worker: State>>) -> Redirect { let target = User::find_by_fqn(&*conn, name.clone()).unwrap(); let f = follows::Follow::insert(&*conn, follows::NewFollow { follower_id: user.id, @@ -86,7 +89,7 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect { act.object_props.set_to_link(target.clone().into_id()).expect("New Follow error while setting 'to'"); act.object_props.set_cc_link_vec::(vec![]).expect("New Follow error while setting 'cc'"); - broadcast(&user, act, vec![target]); + worker.execute(Thunk::of(move || broadcast(&user, act, vec![target]))); Redirect::to(uri!(details: name = name)) } From 5f1de3f9ce41b4c774f0fe9374c63cafb3b60f5d Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 18:23:08 +0200 Subject: [PATCH 065/103] Fix #128 --- templates/blogs/details.html.tera | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index 8a639f39..28c5eab0 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -5,6 +5,10 @@ {{ blog.title }} {% endblock title %} +{% block header %} + {{ blog.title }} +{% endblock header %} + {% block content %}

{{ blog.title }} ~{{ blog.actor_id }}

{{ blog.summary }}

From 7af1faf206b87524188dbfb987dfb016bd02406e Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 18:27:02 +0200 Subject: [PATCH 066/103] Make the "Open on remote instance" button open a new tab --- templates/users/header.html.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/users/header.html.tera b/templates/users/header.html.tera index 8f66eb69..fef43ea3 100644 --- a/templates/users/header.html.tera +++ b/templates/users/header.html.tera @@ -15,7 +15,7 @@ {% endif %} {% if is_remote %} - {{ "Open on {{ instance_url }}" | _(instance_url=instance_url) }} + {{ "Open on {{ instance_url }}" | _(instance_url=instance_url) }} {% endif %} {% set not_self = not is_self %} From 84974897ed282c234f34fcb5a1ae234c50be655e Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 18:36:38 +0200 Subject: [PATCH 067/103] Display FQN on user profiles Fix #130 --- templates/blogs/details.html.tera | 2 +- templates/users/header.html.tera | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index 28c5eab0..26b836b7 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -10,7 +10,7 @@ {% endblock header %} {% block content %} -

{{ blog.title }} ~{{ blog.actor_id }}

+

{{ blog.title }} ~{{ blog.fqn }}

{{ blog.summary }}

{{ "{{ count }} authors in this blog: " | _n(singular="One author in this blog: ", count = n_authors) }} diff --git a/templates/users/header.html.tera b/templates/users/header.html.tera index fef43ea3..11ac6028 100644 --- a/templates/users/header.html.tera +++ b/templates/users/header.html.tera @@ -1,6 +1,9 @@

{{ user.name }} + + @{{ user.fqn }} + {% if user.is_admin %} {{ "Admin" | _ }} {% endif %} From 999e91290a9e61e1f86ad44c3e8f36291d9890d4 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 18:37:13 +0200 Subject: [PATCH 068/103] Link in comments redirect to profile on the current instance Fix #127 --- templates/macros.html.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 50a192e4..b35db055 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -43,7 +43,7 @@ {% endmacro %} {% macro comment(comm) %}
- + {{ comm.author.name }} @{{ comm.author.username }} From b42030e831d3731c55b622f888555308992110fb Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 26 Jul 2018 19:00:23 +0200 Subject: [PATCH 069/103] Try to use only "absolute" links to avoid trailing-slash bugs --- plume-models/src/blogs.rs | 3 +-- src/routes/posts.rs | 2 +- templates/blogs/details.html.tera | 2 +- templates/macros.html.tera | 2 +- templates/posts/details.html.tera | 16 ++++++++-------- templates/users/header.html.tera | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 59991be6..7b1df681 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -259,8 +259,7 @@ impl Blog { pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { let mut json = serde_json::to_value(self).unwrap(); - let formatted = serde_json::Value::String(format!("/~/{}",self.get_fqn(conn))); - json["fqn"] = formatted; + json["fqn"] = json!(self.get_fqn(conn)); json } } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 95016bbb..02ed8bcf 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -41,7 +41,7 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option Template::render("posts/details", json!({ "author": post.get_authors(&*conn)[0].to_json(&*conn), - "post": post, + "article": post.to_json(&*conn), "blog": blog, "comments": &comments.into_iter().filter_map(|c| if c.in_response_to_id.is_none() { Some(c.to_json(&*conn, &comms)) diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index 26b836b7..aa4be52f 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -28,7 +28,7 @@

{{ "No posts to see here yet." | _ }}

{% endif %} {% if is_author %} - {{ "New article" | _ }} + {{ "New article" | _ }} {% endif %}
{% for article in posts %} diff --git a/templates/macros.html.tera b/templates/macros.html.tera index b35db055..07ced606 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -13,7 +13,7 @@ link_4="") }} ⋅ {{ article.date | date(format="%B %e") }} - ⋅ {{ article.blog.title }} + ⋅ {{ article.blog.title }}

{% endmacro post_card %} diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index 0ac3f44f..0cb3cdea 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -2,15 +2,15 @@ {% import "macros" as macros %} {% block title %} -{{ post.title }} +{{ article.post.title }} {% endblock title %} {% block header %} - {{ blog.title }} + {{ blog.title }} {% endblock header %} {% block content %} -

{{ post.title }}

+

{{ article.post.title }}

- {{ post.content | safe }} + {{ article.post.content | safe }}

+ From 826772ca20b28007e9618c568f7cf6313258631a Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 27 Jul 2018 14:30:51 +0200 Subject: [PATCH 078/103] Try to fix #131 --- static/main.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/static/main.css b/static/main.css index 1d616a34..1f8dcd0e 100644 --- a/static/main.css +++ b/static/main.css @@ -27,6 +27,8 @@ small { color: #242424; opacity: 0.6; font-size: 0.75em; + word-wrap: break-word; + word-break: break-all; } .center { @@ -464,6 +466,8 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } box-sizing: border-box; background: #E3E3E3; + + text-overflow: ellipsis; } .list .card { /* TODO */ From c3b19b3ffc89cc3ede8b1a123bac67ea446311c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Fri, 27 Jul 2018 15:05:46 +0200 Subject: [PATCH 079/103] i18n: Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- po/pl.po | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/po/pl.po b/po/pl.po index 3f792357..6c34a27d 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: plume\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-06-15 16:33-0700\n" -"PO-Revision-Date: 2018-07-20 20:55+0200\n" +"PO-Revision-Date: 2018-07-27 15:05+0200\n" "Last-Translator: Marcin Mikołajczak \n" "Language-Team: none\n" "Language: pl\n" @@ -343,44 +343,38 @@ msgstr "" msgid "Optional" msgstr "Nieobowiązkowe" -#, fuzzy msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" -msgstr[0] "Ten blog ma jednego autora: " -msgstr[1] "Ten blog ma {{ count }} autorów: " -msgstr[2] "Ten blog ma {{ count }} autorów: " +msgstr[0] "Jeden artykuł na tym blogu" +msgstr[1] "{{ count }} artykuły na tym blogu" +msgstr[2] "{{ count }} artykułów na tym blogu" msgid "Previous page" -msgstr "" +msgstr "Poprzednia strona" msgid "Next page" -msgstr "" +msgstr "Następna strona" -#, fuzzy msgid "{{ user }} mentioned you." -msgstr "{{ data }} wspomniał o Tobie." +msgstr "{{ user }} wspomniał o Tobie." -#, fuzzy msgid "{{ user }} commented your article." -msgstr "{{ data }} skomentował Twój artykuł" +msgstr "{{ user }} skomentował Twój artykuł." -#, fuzzy msgid "{{ user }} is now following you." -msgstr "{{ data }} zaczął Cię obserwować" +msgstr "{{ user }} zaczął Cię obserwować." -#, fuzzy msgid "{{ user }} liked your article." -msgstr "{{ data }} polubił Twój artykuł" +msgstr "{{ user }} polubił Twój artykuł." -#, fuzzy msgid "{{ user }} reshared your article." -msgstr "{{ data }} udostępnił Twój artykuł" +msgstr "{{ user }} udostępnił Twój artykuł." msgid "Source code" -msgstr "" +msgstr "Kod źródłowy" msgid "Matrix room" -msgstr "" +msgstr "Pokój Matrix.org" #~ msgid "Logowanie" #~ msgstr "Zaloguj się" From 74ec59e77cd8cbb76e8a72302a9557d2324eef54 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 27 Jul 2018 19:05:36 +0200 Subject: [PATCH 080/103] Add some configuration options for instance admins --- .../down.sql | 5 +++ .../up.sql | 5 +++ plume-models/src/admin.rs | 19 ++++++++ plume-models/src/blogs.rs | 7 ++- plume-models/src/instance.rs | 23 +++++++++- plume-models/src/lib.rs | 1 + plume-models/src/schema.rs | 4 ++ plume-models/src/users.rs | 7 ++- po/de.po | 6 +++ po/en.po | 6 +++ po/fr.po | 6 +++ po/pl.po | 6 +++ po/plume.pot | 6 +++ src/main.rs | 2 + src/routes/instance.rs | 45 +++++++++++++++++++ src/setup.rs | 6 ++- static/main.css | 9 +++- templates/base.html.tera | 5 +++ templates/instance/admin.html.tera | 34 ++++++++++++++ templates/macros.html.tera | 5 ++- templates/users/edit.html.tera | 2 +- 21 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 migrations/2018-07-27-125558_instance_customization/down.sql create mode 100644 migrations/2018-07-27-125558_instance_customization/up.sql create mode 100644 plume-models/src/admin.rs create mode 100644 templates/instance/admin.html.tera diff --git a/migrations/2018-07-27-125558_instance_customization/down.sql b/migrations/2018-07-27-125558_instance_customization/down.sql new file mode 100644 index 00000000..38cca3cd --- /dev/null +++ b/migrations/2018-07-27-125558_instance_customization/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE instances DROP COLUMN open_registrations; +ALTER TABLE instances DROP COLUMN short_description; +ALTER TABLE instances DROP COLUMN long_description; +ALTER TABLE instances DROP COLUMN default_license; diff --git a/migrations/2018-07-27-125558_instance_customization/up.sql b/migrations/2018-07-27-125558_instance_customization/up.sql new file mode 100644 index 00000000..1efde74c --- /dev/null +++ b/migrations/2018-07-27-125558_instance_customization/up.sql @@ -0,0 +1,5 @@ +-- Your SQL goes here +ALTER TABLE instances ADD COLUMN open_registrations BOOLEAN NOT NULL DEFAULT 't'; +ALTER TABLE instances ADD COLUMN short_description TEXT NOT NULL DEFAULT ''; +ALTER TABLE instances ADD COLUMN long_description TEXT NOT NULL DEFAULT ''; +ALTER TABLE instances ADD COLUMN default_license TEXT NOT NULL DEFAULT 'CC-0'; diff --git a/plume-models/src/admin.rs b/plume-models/src/admin.rs new file mode 100644 index 00000000..8d85c3bd --- /dev/null +++ b/plume-models/src/admin.rs @@ -0,0 +1,19 @@ +use rocket::{Outcome, http::Status, request::{self, FromRequest, Request}}; + +use users::User; + +/// Wrapper around User to use as a request guard on pages reserved to admins. +pub struct Admin(pub User); + +impl<'a, 'r> FromRequest<'a, 'r> for Admin { + type Error = (); + + fn from_request(request: &'a Request<'r>) -> request::Outcome { + let user = request.guard::()?; + if user.is_admin { + Outcome::Success(Admin(user)) + } else { + Outcome::Failure((Status::Unauthorized, ())) + } + } +} diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index be8f544d..324b60ec 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -140,7 +140,12 @@ impl Blog { Instance::insert(conn, NewInstance { public_domain: inst.clone(), name: inst.clone(), - local: false + local: false, + // We don't really care about all the following for remote instances + long_description: String::new(), + short_description: String::new(), + default_license: String::new(), + open_registrations: true }) } }; diff --git a/plume-models/src/instance.rs b/plume-models/src/instance.rs index 8eef2254..36f38470 100644 --- a/plume-models/src/instance.rs +++ b/plume-models/src/instance.rs @@ -13,7 +13,11 @@ pub struct Instance { pub name: String, pub local: bool, pub blocked: bool, - pub creation_date: NaiveDateTime + pub creation_date: NaiveDateTime, + pub open_registrations: bool, + pub short_description: String, + pub long_description: String, + pub default_license : String } #[derive(Insertable)] @@ -21,7 +25,11 @@ pub struct Instance { pub struct NewInstance { pub public_domain: String, pub name: String, - pub local: bool + pub local: bool, + pub open_registrations: bool, + pub short_description: String, + pub long_description: String, + pub default_license : String } impl Instance { @@ -68,4 +76,15 @@ impl Instance { box_name = box_name )) } + + pub fn update(&self, conn: &PgConnection, name: String, open_registrations: bool, short_description: String, long_description: String) -> Instance { + diesel::update(self) + .set(( + instances::name.eq(name), + instances::open_registrations.eq(open_registrations), + instances::short_description.eq(short_description), + instances::long_description.eq(long_description), + )).get_result::(conn) + .expect("Couldn't update instance") + } } diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index 5d15fc2c..e0bd5b20 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -103,6 +103,7 @@ pub fn ap_url(url: String) -> String { format!("{}://{}", scheme, url) } +pub mod admin; pub mod blog_authors; pub mod blogs; pub mod comments; diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index fce52e1e..47ed12dd 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -53,6 +53,10 @@ table! { local -> Bool, blocked -> Bool, creation_date -> Timestamp, + open_registrations -> Bool, + short_description -> Text, + long_description -> Text, + default_license -> Text, } } diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 332d7580..9878cec9 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -184,7 +184,12 @@ impl User { Instance::insert(conn, NewInstance { name: inst.clone(), public_domain: inst.clone(), - local: false + local: false, + // We don't really care about all the following for remote instances + long_description: String::new(), + short_description: String::new(), + default_license: String::new(), + open_registrations: true }) } }; diff --git a/po/de.po b/po/de.po index 921b90c3..907385db 100644 --- a/po/de.po +++ b/po/de.po @@ -377,3 +377,9 @@ msgstr "" msgid "Matrix room" msgstr "" + +msgid "Administration" +msgstr "" + +msgid "Instance settings" +msgstr "" diff --git a/po/en.po b/po/en.po index bc06fe64..6b46a0b8 100644 --- a/po/en.po +++ b/po/en.po @@ -364,3 +364,9 @@ msgstr "" msgid "Matrix room" msgstr "" + +msgid "Administration" +msgstr "" + +msgid "Instance settings" +msgstr "" diff --git a/po/fr.po b/po/fr.po index c5a80b64..f6d0a96f 100644 --- a/po/fr.po +++ b/po/fr.po @@ -373,3 +373,9 @@ msgstr "" msgid "Matrix room" msgstr "" + +msgid "Administration" +msgstr "" + +msgid "Instance settings" +msgstr "" diff --git a/po/pl.po b/po/pl.po index 3f792357..37a8fd29 100644 --- a/po/pl.po +++ b/po/pl.po @@ -382,5 +382,11 @@ msgstr "" msgid "Matrix room" msgstr "" +msgid "Administration" +msgstr "" + +msgid "Instance settings" +msgstr "" + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/po/plume.pot b/po/plume.pot index 5617ce09..58b1ea3a 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -357,3 +357,9 @@ msgstr "" msgid "Matrix room" msgstr "" + +msgid "Administration" +msgstr "" + +msgid "Instance settings" +msgstr "" diff --git a/src/main.rs b/src/main.rs index 463c857a..2e0c76fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,8 @@ fn main() { routes::instance::paginated_index, routes::instance::index, + routes::instance::admin, + routes::instance::update_settings, routes::instance::shared_inbox, routes::instance::nodeinfo, diff --git a/src/routes/instance.rs b/src/routes/instance.rs index 475d69cf..e3b46d90 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -1,8 +1,11 @@ use gettextrs::gettext; +use rocket::{request::LenientForm, response::Redirect}; use rocket_contrib::{Json, Template}; use serde_json; +use validator::{Validate}; use plume_models::{ + admin::Admin, comments::Comment, db_conn::DbConn, posts::Post, @@ -39,6 +42,48 @@ fn index(conn: DbConn, user: Option) -> Template { paginated_index(conn, user, Page::first()) } +#[get("/admin")] +fn admin(conn: DbConn, admin: Admin) -> Template { + Template::render("instance/admin", json!({ + "account": admin.0, + "instance": Instance::get_local(&*conn), + "errors": null, + "form": null + })) +} + +#[derive(FromForm, Validate, Serialize)] +struct InstanceSettingsForm { + #[validate(length(min = "1"))] + name: String, + open_registrations: bool, + short_description: String, + long_description: String, + #[validate(length(min = "1"))] + default_license: String +} + +#[post("/admin", data = "")] +fn update_settings(conn: DbConn, admin: Admin, form: LenientForm) -> Result { + let form = form.get(); + form.validate() + .map(|_| { + let instance = Instance::get_local(&*conn).unwrap(); + instance.update(&*conn, + form.name.clone(), + form.open_registrations, + form.short_description.clone(), + form.long_description.clone()); + Redirect::to(uri!(admin)) + }) + .map_err(|e| Template::render("instance/admin", json!({ + "account": admin.0, + "instance": Instance::get_local(&*conn), + "errors": e.inner(), + "form": form + }))) +} + #[post("/inbox", data = "")] fn shared_inbox(conn: DbConn, data: String) -> String { let act: serde_json::Value = serde_json::from_str(&data[..]).unwrap(); diff --git a/src/setup.rs b/src/setup.rs index 45378eed..1da87610 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -151,7 +151,11 @@ fn quick_setup(conn: DbConn) { let instance = Instance::insert(&*conn, NewInstance { public_domain: domain, name: name, - local: true + local: true, + long_description: String::new(), + short_description: String::new(), + default_license: String::from("CC-0"), + open_registrations: true }); println!("{}\n", " ✔️ Your instance was succesfully created!".green()); diff --git a/static/main.css b/static/main.css index 1f8dcd0e..481291fe 100644 --- a/static/main.css +++ b/static/main.css @@ -362,7 +362,14 @@ textarea { font-size: 1.1em; line-height: 1.5em; } - + +input[type="checkbox"] { + display: inline; + margin: initial; + min-width: initial; + width: initial; +} + /* Button & Submit */ .button, input[type="submit"], button { diff --git a/templates/base.html.tera b/templates/base.html.tera index 5f4fed4f..f5c19712 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -36,6 +36,11 @@ Plume 0.1.0 {{ "Source code" | _ }} {{ "Matrix room" | _ }} + {% if account %} + {% if account.is_admin %} + {{ "Administration" | _ }} + {% endif %} + {% endif %} diff --git a/templates/instance/admin.html.tera b/templates/instance/admin.html.tera new file mode 100644 index 00000000..14b4e816 --- /dev/null +++ b/templates/instance/admin.html.tera @@ -0,0 +1,34 @@ +{% extends "base" %} +{% import "macros" as macros %} + +{% block title %} +Administration of {{ instance.name }} +{% endblock title %} + +{% block content %} +

{{ "Administration" | _ }}

+ +

{{ "Instance settings" | _ }}

+ + {{ macros::input(name="name", label="Name", errors=errors, form=form, props='minlenght="1"', default=instance) }} + + + + + + + + + + {{ macros::input(name="default_license", label="Default license", errors=errors, form=form, props='minlenght="1"', default=instance) }} + + + +{% endblock content %} diff --git a/templates/macros.html.tera b/templates/macros.html.tera index 07ced606..0f9dbf69 100644 --- a/templates/macros.html.tera +++ b/templates/macros.html.tera @@ -17,7 +17,7 @@

{% endmacro post_card %} -{% macro input(name, label, errors, form, type="text", props="", optional=false) %} +{% macro input(name, label, errors, form, type="text", props="", optional=false, default='') %}
{% endblock content %} From 0926a1d3b5eb0e0ce910fab87420ec0acc89e176 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 27 Jul 2018 20:54:34 +0200 Subject: [PATCH 084/103] Tell that we support Markdown Fixes #122 --- templates/instance/admin.html.tera | 4 ++-- templates/posts/new.html.tera | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/instance/admin.html.tera b/templates/instance/admin.html.tera index 14b4e816..5c77c32a 100644 --- a/templates/instance/admin.html.tera +++ b/templates/instance/admin.html.tera @@ -21,10 +21,10 @@ Administration of {{ instance.name }} {{ "Allow anyone to register" | _ }} - + - + {{ macros::input(name="default_license", label="Default license", errors=errors, form=form, props='minlenght="1"', default=instance) }} diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera index f1778c1f..9a4b2972 100644 --- a/templates/posts/new.html.tera +++ b/templates/posts/new.html.tera @@ -16,7 +16,7 @@ {% endfor %} {% endif %} - + {% set license_infos = "Default license will be {{ instance.default_license }}" | _(instance=instance) %} From ed8982b7fd69bc99be315c01667f98b286e9d962 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 27 Jul 2018 22:16:17 +0200 Subject: [PATCH 085/103] Add a presentation of Plume and of the instance on the homepage Fixes #132 --- .../down.sql | 3 ++ .../up.sql | 3 ++ plume-models/src/blogs.rs | 4 +- plume-models/src/instance.rs | 13 +++++- plume-models/src/schema.rs | 2 + plume-models/src/users.rs | 4 +- po/de.po | 41 +++++++++++++++++++ po/en.po | 39 ++++++++++++++++++ po/fr.po | 41 +++++++++++++++++++ po/pl.po | 41 +++++++++++++++++++ po/plume.pot | 36 ++++++++++++++++ src/routes/instance.rs | 4 +- src/setup.rs | 4 +- static/main.css | 32 +++++++++++++++ templates/base.html.tera | 1 + templates/instance/index.html.tera | 33 +++++++++++++++ 16 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 migrations/2018-07-27-194816_instance_description_html/down.sql create mode 100644 migrations/2018-07-27-194816_instance_description_html/up.sql diff --git a/migrations/2018-07-27-194816_instance_description_html/down.sql b/migrations/2018-07-27-194816_instance_description_html/down.sql new file mode 100644 index 00000000..99f0b18e --- /dev/null +++ b/migrations/2018-07-27-194816_instance_description_html/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE instances DROP COLUMN long_description_html; +ALTER TABLE instances DROP COLUMN short_description_html; diff --git a/migrations/2018-07-27-194816_instance_description_html/up.sql b/migrations/2018-07-27-194816_instance_description_html/up.sql new file mode 100644 index 00000000..47cbee03 --- /dev/null +++ b/migrations/2018-07-27-194816_instance_description_html/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE instances ADD COLUMN long_description_html VARCHAR NOT NULL DEFAULT ''; +ALTER TABLE instances ADD COLUMN short_description_html VARCHAR NOT NULL DEFAULT ''; diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 324b60ec..a992f748 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -145,7 +145,9 @@ impl Blog { long_description: String::new(), short_description: String::new(), default_license: String::new(), - open_registrations: true + open_registrations: true, + short_description_html: String::new(), + long_description_html: String::new() }) } }; diff --git a/plume-models/src/instance.rs b/plume-models/src/instance.rs index 36f38470..5cb2a3fe 100644 --- a/plume-models/src/instance.rs +++ b/plume-models/src/instance.rs @@ -2,6 +2,7 @@ use chrono::NaiveDateTime; use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection}; use std::iter::Iterator; +use plume_common::utils::md_to_html; use ap_url; use users::User; use schema::{instances, users}; @@ -17,7 +18,9 @@ pub struct Instance { pub open_registrations: bool, pub short_description: String, pub long_description: String, - pub default_license : String + pub default_license : String, + pub long_description_html: String, + pub short_description_html: String } #[derive(Insertable)] @@ -29,7 +32,9 @@ pub struct NewInstance { pub open_registrations: bool, pub short_description: String, pub long_description: String, - pub default_license : String + pub default_license : String, + pub long_description_html: String, + pub short_description_html: String } impl Instance { @@ -78,12 +83,16 @@ impl Instance { } pub fn update(&self, conn: &PgConnection, name: String, open_registrations: bool, short_description: String, long_description: String) -> Instance { + let (sd, _) = md_to_html(short_description.as_ref()); + let (ld, _) = md_to_html(long_description.as_ref()); diesel::update(self) .set(( instances::name.eq(name), instances::open_registrations.eq(open_registrations), instances::short_description.eq(short_description), instances::long_description.eq(long_description), + instances::short_description_html.eq(sd), + instances::long_description_html.eq(ld) )).get_result::(conn) .expect("Couldn't update instance") } diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index 47ed12dd..79807097 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -57,6 +57,8 @@ table! { short_description -> Text, long_description -> Text, default_license -> Text, + long_description_html -> Varchar, + short_description_html -> Varchar, } } diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 9878cec9..ad0373b7 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -189,7 +189,9 @@ impl User { long_description: String::new(), short_description: String::new(), default_license: String::new(), - open_registrations: true + open_registrations: true, + short_description_html: String::new(), + long_description_html: String::new() }) } }; diff --git a/po/de.po b/po/de.po index 3f7117a3..020a7e07 100644 --- a/po/de.po +++ b/po/de.po @@ -404,3 +404,44 @@ msgstr "" msgid "No comments yet. Be the first to react!" msgstr "" + +msgid "About this instance" +msgstr "" + +msgid "What is Plume?" +msgstr "" + +msgid "Plume is a decentralized blogging engine." +msgstr "" + +msgid "Authors can manage various blogs from an unique website." +msgstr "" + +msgid "" +"Articles are also visible on other Plume websites, and you can interact with " +"them directly from other platforms like Mastodon." +msgstr "" + +#, fuzzy +msgid "Create your account" +msgstr "Account erstellen" + +#, fuzzy +msgid "About {{ instance_name }}" +msgstr "Öffnen auf {{ instance_url }}" + +msgid "Home to" +msgstr "" + +msgid "people" +msgstr "" + +msgid "Who wrote" +msgstr "" + +#, fuzzy +msgid "articles" +msgstr "Neuer Artikel" + +msgid "Read the detailed rules" +msgstr "" diff --git a/po/en.po b/po/en.po index de5917b1..65f3682f 100644 --- a/po/en.po +++ b/po/en.po @@ -391,3 +391,42 @@ msgstr "" msgid "No comments yet. Be the first to react!" msgstr "" + +msgid "About this instance" +msgstr "" + +msgid "What is Plume?" +msgstr "" + +msgid "Plume is a decentralized blogging engine." +msgstr "" + +msgid "Authors can manage various blogs from an unique website." +msgstr "" + +msgid "" +"Articles are also visible on other Plume websites, and you can interact with " +"them directly from other platforms like Mastodon." +msgstr "" + +msgid "Create your account" +msgstr "" + +#, fuzzy +msgid "About {{ instance_name }}" +msgstr "Welcome on {{ instance_name }}" + +msgid "Home to" +msgstr "" + +msgid "people" +msgstr "" + +msgid "Who wrote" +msgstr "" + +msgid "articles" +msgstr "" + +msgid "Read the detailed rules" +msgstr "" diff --git a/po/fr.po b/po/fr.po index 30abf90d..c99365f5 100644 --- a/po/fr.po +++ b/po/fr.po @@ -400,3 +400,44 @@ msgstr "" msgid "No comments yet. Be the first to react!" msgstr "" + +msgid "About this instance" +msgstr "" + +msgid "What is Plume?" +msgstr "" + +msgid "Plume is a decentralized blogging engine." +msgstr "" + +msgid "Authors can manage various blogs from an unique website." +msgstr "" + +msgid "" +"Articles are also visible on other Plume websites, and you can interact with " +"them directly from other platforms like Mastodon." +msgstr "" + +#, fuzzy +msgid "Create your account" +msgstr "Créer mon compte" + +#, fuzzy +msgid "About {{ instance_name }}" +msgstr "Ouvrir sur {{ instance_url }}" + +msgid "Home to" +msgstr "" + +msgid "people" +msgstr "" + +msgid "Who wrote" +msgstr "" + +#, fuzzy +msgid "articles" +msgstr "Nouvel article" + +msgid "Read the detailed rules" +msgstr "" diff --git a/po/pl.po b/po/pl.po index 72f3f36b..25dff52c 100644 --- a/po/pl.po +++ b/po/pl.po @@ -403,5 +403,46 @@ msgstr "" msgid "No comments yet. Be the first to react!" msgstr "" +msgid "About this instance" +msgstr "" + +msgid "What is Plume?" +msgstr "" + +msgid "Plume is a decentralized blogging engine." +msgstr "" + +msgid "Authors can manage various blogs from an unique website." +msgstr "" + +msgid "" +"Articles are also visible on other Plume websites, and you can interact with " +"them directly from other platforms like Mastodon." +msgstr "" + +#, fuzzy +msgid "Create your account" +msgstr "Utwórz konto" + +#, fuzzy +msgid "About {{ instance_name }}" +msgstr "Otwórz na {{ instance_url }}" + +msgid "Home to" +msgstr "" + +msgid "people" +msgstr "" + +msgid "Who wrote" +msgstr "" + +#, fuzzy +msgid "articles" +msgstr "Nowy artykuł" + +msgid "Read the detailed rules" +msgstr "" + #~ msgid "Logowanie" #~ msgstr "Zaloguj się" diff --git a/po/plume.pot b/po/plume.pot index 41689c2f..d2a32d84 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -384,3 +384,39 @@ msgstr "" msgid "No comments yet. Be the first to react!" msgstr "" + +msgid "About this instance" +msgstr "" + +msgid "What is Plume?" +msgstr "" + +msgid "Plume is a decentralized blogging engine." +msgstr "" + +msgid "Authors can manage various blogs from an unique website." +msgstr "" + +msgid "Articles are also visible on other Plume websites, and you can interact with them directly from other platforms like Mastodon." +msgstr "" + +msgid "Create your account" +msgstr "" + +msgid "About {{ instance_name }}" +msgstr "" + +msgid "Home to" +msgstr "" + +msgid "people" +msgstr "" + +msgid "Who wrote" +msgstr "" + +msgid "articles" +msgstr "" + +msgid "Read the detailed rules" +msgstr "" diff --git a/src/routes/instance.rs b/src/routes/instance.rs index e3b46d90..cf8ec983 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -26,7 +26,9 @@ fn paginated_index(conn: DbConn, user: Option, page: Page) -> Template { "account": user, "recents": recents.into_iter().map(|p| p.to_json(&*conn)).collect::>(), "page": page.page, - "n_pages": Page::total(Post::count(&*conn) as i32) + "n_pages": Page::total(Post::count(&*conn) as i32), + "n_users": User::count_local(&*conn), + "n_articles": Post::count_local(&*conn) })) } None => { diff --git a/src/setup.rs b/src/setup.rs index 1da87610..8efd038d 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -155,7 +155,9 @@ fn quick_setup(conn: DbConn) { long_description: String::new(), short_description: String::new(), default_license: String::from("CC-0"), - open_registrations: true + open_registrations: true, + short_description_html: String::new(), + long_description_html: String::new() }); println!("{}\n", " ✔️ Your instance was succesfully created!".green()); diff --git a/static/main.css b/static/main.css index 481291fe..0ed247d8 100644 --- a/static/main.css +++ b/static/main.css @@ -38,6 +38,10 @@ small { padding: 5em; } +.spaced { + margin: 4rem 0; +} + /* * == Header == */ @@ -509,6 +513,34 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } overflow: hidden; } +/* Presentation */ +.presentation > h2, .presentation > a { + text-align: center; +} + +.presentation > a { + font-size: 1.2em; + margin: 1em; +} + +/* Stats */ +.stats { + display: flex; + justify-content: space-around; + margin: 2em; +} + +.stats > div { + display: flex; + flex-direction: column; + text-align: center; +} + +.stats em { + text-align: center; + font-weight: bold; +} + /* ================= * * Small Screens * * ================= */ diff --git a/templates/base.html.tera b/templates/base.html.tera index f5c19712..5ec261e3 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -34,6 +34,7 @@