mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-26 13:31:02 +00:00
Merge branch 'master' of https://github.com/Plume-org/Plume into icons
This commit is contained in:
commit
a30b99f93e
25 changed files with 299 additions and 108 deletions
22
docs/ENV-VARS.md
Normal file
22
docs/ENV-VARS.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Useful Environment Variables
|
||||||
|
|
||||||
|
Plume relies on some environment variables for some configuration options. You can either set them before
|
||||||
|
starting the app with `cargo run` or write them in a `.env` file to have automatically loaded.
|
||||||
|
|
||||||
|
Here are the variables that Plume uses:
|
||||||
|
|
||||||
|
- `BASE_URL`: the domain name, or IP and port on which Plume is listening. It is used in all federation-related code.
|
||||||
|
- `DB_URL`: the URL of the PostgreSQL database, used by Plume (`postgres://plume:plume@localhost/plume` by default).
|
||||||
|
- `POSTGRES_USER`: if you just want to use a different PostgreSQL user name, and keep the rest of the default URL.
|
||||||
|
- `POSTGRES_PASSWORD`: same as `POSTGRES_USER`, but for the password.
|
||||||
|
- `USE_HTTPS`: if it is `0`, federation and medias will be using HTTP by default (`1` by default).
|
||||||
|
- `ROCKET_ADDRESS`: the adress on which Plume should listen (`0.0.0.0` by default).
|
||||||
|
- `ROCKET_PORT`: the port on which Plume should listen ([`7878` by default](https://twitter.com/ag_dubs/status/852559264510070784))
|
||||||
|
- `ROCKET_SECRET_KEY`: key used to sign private cookies and for CSRF protection. If it is not set, it will be regenerated everytime you restart Plume,
|
||||||
|
meaning that all your users will get disconnected. You can generate one with `openssl rand -base64 32`.
|
||||||
|
|
||||||
|
## Diesel
|
||||||
|
|
||||||
|
Diesel, the tool we use to run migrations may be configured with the `DATABASE_URL` which should contain the URL of the
|
||||||
|
PostgreSQL database. Otherwise, you can specify `--database-url YOUR-URL` everytime you run a `diesel` command.
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
|
|
||||||
- [Installing Plume (for development or production)](INSTALL.md)
|
- [Installing Plume (for development or production)](INSTALL.md)
|
||||||
- [Updating your instance](UPDATE.md)
|
- [Updating your instance](UPDATE.md)
|
||||||
|
- [Useful Environment Variables](ENV-VARS.md)
|
||||||
- [Development Guide](DEVELOPMENT.md)
|
- [Development Guide](DEVELOPMENT.md)
|
||||||
- [Making Plume available in your language](INTERNATIONALIZATION.md)
|
- [Making Plume available in your language](INTERNATIONALIZATION.md)
|
||||||
|
|
2
migrations/2018-09-04-103017_follows_add_ap_url/down.sql
Normal file
2
migrations/2018-09-04-103017_follows_add_ap_url/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
ALTER TABLE follows DROP COLUMN ap_url;
|
2
migrations/2018-09-04-103017_follows_add_ap_url/up.sql
Normal file
2
migrations/2018-09-04-103017_follows_add_ap_url/up.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE follows ADD COLUMN ap_url TEXT NOT NULL DEFAULT '';
|
2
migrations/2018-09-04-104828_posts_add_subtitle/down.sql
Normal file
2
migrations/2018-09-04-104828_posts_add_subtitle/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
ALTER TABLE posts DROP COLUMN subtitle;
|
2
migrations/2018-09-04-104828_posts_add_subtitle/up.sql
Normal file
2
migrations/2018-09-04-104828_posts_add_subtitle/up.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE posts ADD COLUMN subtitle TEXT NOT NULL DEFAULT '';
|
|
@ -1,7 +1,7 @@
|
||||||
use activitypub::{Actor, activity::{Accept, Follow as FollowAct}, actor::Person};
|
use activitypub::{Actor, activity::{Accept, Follow as FollowAct, Undo}, actor::Person};
|
||||||
use diesel::{self, PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
|
|
||||||
use plume_common::activity_pub::{broadcast, Id, IntoId, inbox::{FromActivity, Notify, WithInbox}, sign::Signer};
|
use plume_common::activity_pub::{broadcast, Id, IntoId, inbox::{FromActivity, Notify, WithInbox, Deletable}, sign::Signer};
|
||||||
use blogs::Blog;
|
use blogs::Blog;
|
||||||
use notifications::*;
|
use notifications::*;
|
||||||
use users::User;
|
use users::User;
|
||||||
|
@ -12,19 +12,42 @@ use schema::follows;
|
||||||
pub struct Follow {
|
pub struct Follow {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub follower_id: i32,
|
pub follower_id: i32,
|
||||||
pub following_id: i32
|
pub following_id: i32,
|
||||||
|
pub ap_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "follows"]
|
#[table_name = "follows"]
|
||||||
pub struct NewFollow {
|
pub struct NewFollow {
|
||||||
pub follower_id: i32,
|
pub follower_id: i32,
|
||||||
pub following_id: i32
|
pub following_id: i32,
|
||||||
|
pub ap_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Follow {
|
impl Follow {
|
||||||
insert!(follows, NewFollow);
|
insert!(follows, NewFollow);
|
||||||
get!(follows);
|
get!(follows);
|
||||||
|
find_by!(follows, find_by_ap_url, ap_url as String);
|
||||||
|
|
||||||
|
pub fn find(conn: &PgConnection, from: i32, to: i32) -> Option<Follow> {
|
||||||
|
follows::table.filter(follows::follower_id.eq(from))
|
||||||
|
.filter(follows::following_id.eq(to))
|
||||||
|
.get_result(conn)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_activity(&self, conn: &PgConnection) -> FollowAct {
|
||||||
|
let user = User::get(conn, self.follower_id).unwrap();
|
||||||
|
let target = User::get(conn, self.following_id).unwrap();
|
||||||
|
|
||||||
|
let mut act = FollowAct::default();
|
||||||
|
act.follow_props.set_actor_link::<Id>(user.clone().into_id()).expect("Follow::into_activity: actor error");
|
||||||
|
act.follow_props.set_object_object(user.into_activity(&*conn)).unwrap();
|
||||||
|
act.object_props.set_id_string(self.ap_url.clone()).unwrap();
|
||||||
|
act.object_props.set_to_link(target.clone().into_id()).expect("New Follow error while setting 'to'");
|
||||||
|
act.object_props.set_cc_link_vec::<Id>(vec![]).expect("New Follow error while setting 'cc'");
|
||||||
|
act
|
||||||
|
}
|
||||||
|
|
||||||
/// from -> The one sending the follow request
|
/// from -> The one sending the follow request
|
||||||
/// target -> The target of the request, responding with Accept
|
/// target -> The target of the request, responding with Accept
|
||||||
|
@ -36,9 +59,12 @@ impl Follow {
|
||||||
from_id: i32,
|
from_id: i32,
|
||||||
target_id: i32
|
target_id: i32
|
||||||
) -> Follow {
|
) -> Follow {
|
||||||
|
let from_url: String = from.clone().into_id().into();
|
||||||
|
let target_url: String = target.clone().into_id().into();
|
||||||
let res = Follow::insert(conn, NewFollow {
|
let res = Follow::insert(conn, NewFollow {
|
||||||
follower_id: from_id,
|
follower_id: from_id,
|
||||||
following_id: target_id
|
following_id: target_id,
|
||||||
|
ap_url: format!("{}/follow/{}", from_url, target_url),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut accept = Accept::default();
|
let mut accept = Accept::default();
|
||||||
|
@ -77,3 +103,21 @@ impl Notify<PgConnection> for Follow {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deletable<PgConnection, Undo> for Follow {
|
||||||
|
fn delete(&self, conn: &PgConnection) -> Undo {
|
||||||
|
diesel::delete(self).execute(conn).expect("Coudn't delete follow");
|
||||||
|
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
undo.undo_props.set_actor_link(User::get(conn, self.follower_id).unwrap().into_id()).expect("Follow::delete: actor error");
|
||||||
|
undo.object_props.set_id_string(format!("{}/undo", self.ap_url)).expect("Follow::delete: id error");
|
||||||
|
undo.undo_props.set_object_object(self.into_activity(conn)).expect("Follow::delete: object error");
|
||||||
|
undo
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_id(id: String, conn: &PgConnection) {
|
||||||
|
if let Some(follow) = Follow::find_by_ap_url(conn, id) {
|
||||||
|
follow.delete(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,20 +33,22 @@ pub struct Post {
|
||||||
pub published: bool,
|
pub published: bool,
|
||||||
pub license: String,
|
pub license: String,
|
||||||
pub creation_date: NaiveDateTime,
|
pub creation_date: NaiveDateTime,
|
||||||
pub ap_url: String
|
pub ap_url: String,
|
||||||
|
pub subtitle: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "posts"]
|
#[table_name = "posts"]
|
||||||
pub struct NewPost {
|
pub struct NewPost {
|
||||||
pub blog_id: i32,
|
pub blog_id: i32,
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub content: SafeString,
|
pub content: SafeString,
|
||||||
pub published: bool,
|
pub published: bool,
|
||||||
pub license: String,
|
pub license: String,
|
||||||
pub creation_date: Option<NaiveDateTime>,
|
pub creation_date: Option<NaiveDateTime>,
|
||||||
pub ap_url: String
|
pub ap_url: String,
|
||||||
|
pub subtitle: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Post {
|
impl Post {
|
||||||
|
@ -185,6 +187,7 @@ impl Post {
|
||||||
article.object_props.set_attributed_to_link_vec::<Id>(authors).expect("Article::into_activity: attributedTo error");
|
article.object_props.set_attributed_to_link_vec::<Id>(authors).expect("Article::into_activity: attributedTo error");
|
||||||
article.object_props.set_content_string(self.content.get().clone()).expect("Article::into_activity: content error");
|
article.object_props.set_content_string(self.content.get().clone()).expect("Article::into_activity: content error");
|
||||||
article.object_props.set_published_utctime(Utc.from_utc_datetime(&self.creation_date)).expect("Article::into_activity: published error");
|
article.object_props.set_published_utctime(Utc.from_utc_datetime(&self.creation_date)).expect("Article::into_activity: published error");
|
||||||
|
article.object_props.set_summary_string(self.subtitle.clone()).expect("Article::into_activity: summary error");
|
||||||
article.object_props.set_tag_link_vec(mentions).expect("Article::into_activity: tag error");
|
article.object_props.set_tag_link_vec(mentions).expect("Article::into_activity: tag error");
|
||||||
article.object_props.set_url_string(self.ap_url.clone()).expect("Article::into_activity: url error");
|
article.object_props.set_url_string(self.ap_url.clone()).expect("Article::into_activity: url error");
|
||||||
article.object_props.set_to_link_vec::<Id>(to.into_iter().map(Id::new).collect()).expect("Article::into_activity: to error");
|
article.object_props.set_to_link_vec::<Id>(to.into_iter().map(Id::new).collect()).expect("Article::into_activity: to error");
|
||||||
|
@ -250,7 +253,8 @@ impl FromActivity<Article, PgConnection> for Post {
|
||||||
license: String::from("CC-0"), // TODO
|
license: String::from("CC-0"), // TODO
|
||||||
// FIXME: This is wrong: with this logic, we may use the display URL as the AP ID. We need two different fields
|
// FIXME: This is wrong: with this logic, we may use the display URL as the AP ID. We need two different fields
|
||||||
ap_url: article.object_props.url_string().unwrap_or(article.object_props.id_string().expect("Post::from_activity: url + id error")),
|
ap_url: article.object_props.url_string().unwrap_or(article.object_props.id_string().expect("Post::from_activity: url + id error")),
|
||||||
creation_date: Some(article.object_props.published_utctime().expect("Post::from_activity: published error").naive_utc())
|
creation_date: Some(article.object_props.published_utctime().expect("Post::from_activity: published error").naive_utc()),
|
||||||
|
subtitle: article.object_props.summary_string().expect("Post::from_activity: summary error")
|
||||||
});
|
});
|
||||||
|
|
||||||
for author in authors.into_iter() {
|
for author in authors.into_iter() {
|
||||||
|
|
|
@ -42,6 +42,7 @@ table! {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
follower_id -> Int4,
|
follower_id -> Int4,
|
||||||
following_id -> Int4,
|
following_id -> Int4,
|
||||||
|
ap_url -> Text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +125,7 @@ table! {
|
||||||
license -> Varchar,
|
license -> Varchar,
|
||||||
creation_date -> Timestamp,
|
creation_date -> Timestamp,
|
||||||
ap_url -> Varchar,
|
ap_url -> Varchar,
|
||||||
|
subtitle -> Text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -567,7 +567,7 @@ impl WithInbox for User {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_local(&self) -> bool {
|
fn is_local(&self) -> bool {
|
||||||
self.instance_id == 0
|
self.instance_id == 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
po/de.po
10
po/de.po
|
@ -508,5 +508,15 @@ msgid ""
|
||||||
"Sorry, but registrations are closed on this instance. Try to find another one"
|
"Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr "Titel"
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#~ msgid "Your password should be at least 8 characters long"
|
#~ msgid "Your password should be at least 8 characters long"
|
||||||
#~ msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein"
|
#~ msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein"
|
||||||
|
|
9
po/en.po
9
po/en.po
|
@ -497,3 +497,12 @@ msgstr ""
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sorry, but registrations are closed on this instance. Try to find another one"
|
"Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr ""
|
||||||
|
|
14
po/fr.po
14
po/fr.po
|
@ -503,4 +503,16 @@ msgstr "Envoyer"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sorry, but registrations are closed on this instance. Try to find another one"
|
"Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr "Désolé, mais les inscriptions sont fermées sur cette instance. Essayez d'en trouver une autre."
|
msgstr ""
|
||||||
|
"Désolé, mais les inscriptions sont fermées sur cette instance. Essayez d'en "
|
||||||
|
"trouver une autre."
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr "Titre"
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr ""
|
||||||
|
|
13
po/gl.po
13
po/gl.po
|
@ -496,4 +496,15 @@ msgstr "Enviar"
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sorry, but registrations are closed on this instance. Try to find another one"
|
"Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Lamentámolo, pero o rexistro en esta instancia está pechado. Inténteo en outra instancia"
|
"Lamentámolo, pero o rexistro en esta instancia está pechado. Inténteo en "
|
||||||
|
"outra instancia"
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr "Título"
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr ""
|
||||||
|
|
10
po/nb.po
10
po/nb.po
|
@ -512,6 +512,16 @@ msgid ""
|
||||||
"Sorry, but registrations are closed on this instance. Try to find another one"
|
"Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, fuzzy
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr "Tittel"
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#~ msgid "One reshare"
|
#~ msgid "One reshare"
|
||||||
#~ msgid_plural "{{ count }} reshares"
|
#~ msgid_plural "{{ count }} reshares"
|
||||||
#~ msgstr[0] "Én deling"
|
#~ msgstr[0] "Én deling"
|
||||||
|
|
108
po/pl.po
108
po/pl.po
|
@ -3,7 +3,7 @@ msgstr ""
|
||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2018-07-28 14:56+0200\n"
|
"PO-Revision-Date: 2018-09-04 17:35+0200\n"
|
||||||
"Last-Translator: Marcin Mikołajczak <me@m4sk.in>\n"
|
"Last-Translator: Marcin Mikołajczak <me@m4sk.in>\n"
|
||||||
"Language-Team: none\n"
|
"Language-Team: none\n"
|
||||||
"Language: pl\n"
|
"Language: pl\n"
|
||||||
|
@ -12,7 +12,7 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||||
"|| n%100>=20) ? 1 : 2);\n"
|
"|| n%100>=20) ? 1 : 2);\n"
|
||||||
"X-Generator: Poedit 2.0.9\n"
|
"X-Generator: Poedit 2.1.1\n"
|
||||||
|
|
||||||
msgid "Latest articles"
|
msgid "Latest articles"
|
||||||
msgstr "Najnowsze artykuły"
|
msgstr "Najnowsze artykuły"
|
||||||
|
@ -92,16 +92,15 @@ msgstr "Dodaj swoje"
|
||||||
|
|
||||||
msgid "One Boost"
|
msgid "One Boost"
|
||||||
msgid_plural "{{ count }} Boosts"
|
msgid_plural "{{ count }} Boosts"
|
||||||
msgstr[0] ""
|
msgstr[0] "Jedno podbicie"
|
||||||
msgstr[1] ""
|
msgstr[1] "{{ count }} podbicia"
|
||||||
msgstr[2] ""
|
msgstr[2] "{{ count }} podbić"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "I don't want to boost this anymore"
|
msgid "I don't want to boost this anymore"
|
||||||
msgstr "Cofnij udostępnienie"
|
msgstr "Cofnij podbicie"
|
||||||
|
|
||||||
msgid "Boost"
|
msgid "Boost"
|
||||||
msgstr ""
|
msgstr "Podbij"
|
||||||
|
|
||||||
msgid "Comments"
|
msgid "Comments"
|
||||||
msgstr "Komentarze"
|
msgstr "Komentarze"
|
||||||
|
@ -165,9 +164,8 @@ msgstr "Obserwuj"
|
||||||
msgid "Unfollow"
|
msgid "Unfollow"
|
||||||
msgstr "Przestań obserwować"
|
msgstr "Przestań obserwować"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Recently boosted"
|
msgid "Recently boosted"
|
||||||
msgstr "Ostatnio udostępniono"
|
msgstr "Ostatnio podbite"
|
||||||
|
|
||||||
msgid "One follower"
|
msgid "One follower"
|
||||||
msgid_plural "{{ count }} followers"
|
msgid_plural "{{ count }} followers"
|
||||||
|
@ -247,9 +245,8 @@ msgstr "Musisz się zalogować, aby zobaczyć swoje powiadomienia"
|
||||||
msgid "You need to be logged in order to write a new post"
|
msgid "You need to be logged in order to write a new post"
|
||||||
msgstr "Musisz się zalogować, aby utworzyć wpis"
|
msgstr "Musisz się zalogować, aby utworzyć wpis"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "You need to be logged in order to boost a post"
|
msgid "You need to be logged in order to boost a post"
|
||||||
msgstr "Musisz się zalogować, aby polubić wpis"
|
msgstr "Musisz się zalogować, aby podbić wpis"
|
||||||
|
|
||||||
msgid "Invalid username or password"
|
msgid "Invalid username or password"
|
||||||
msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
|
msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
|
||||||
|
@ -268,18 +265,17 @@ msgstr ""
|
||||||
"Napisano przez {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}"
|
"Napisano przez {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}"
|
||||||
"{{ link_3 }}"
|
"{{ link_3 }}"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "{{ data }} boosted your article"
|
msgid "{{ data }} boosted your article"
|
||||||
msgstr "{{ data }} skomentował Twój artykuł"
|
msgstr "{{ data }} podbił(a) Twój artykuł"
|
||||||
|
|
||||||
msgid "{{ data }} started following you"
|
msgid "{{ data }} started following you"
|
||||||
msgstr "{{ data }} zaczął Cię obserwować"
|
msgstr "{{ data }} zaczął(-ęła) Cię obserwować"
|
||||||
|
|
||||||
msgid "{{ data }} liked your article"
|
msgid "{{ data }} liked your article"
|
||||||
msgstr "{{ data }} polubił Twój artykuł"
|
msgstr "{{ data }} polubił(a) Twój artykuł"
|
||||||
|
|
||||||
msgid "{{ data }} commented your article"
|
msgid "{{ data }} commented your article"
|
||||||
msgstr "{{ data }} skomentował Twój artykuł"
|
msgstr "{{ data }} skomentował(a) Twój artykuł"
|
||||||
|
|
||||||
msgid "We couldn't find this page."
|
msgid "We couldn't find this page."
|
||||||
msgstr "Nie udało się odnaleźć tej strony."
|
msgstr "Nie udało się odnaleźć tej strony."
|
||||||
|
@ -294,7 +290,7 @@ msgid "You are not author in this blog."
|
||||||
msgstr "Nie jesteś autorem tego bloga."
|
msgstr "Nie jesteś autorem tego bloga."
|
||||||
|
|
||||||
msgid "{{ data }} mentioned you."
|
msgid "{{ data }} mentioned you."
|
||||||
msgstr "{{ data }} wspomniał o Tobie."
|
msgstr "{{ data }} wspomniał(a) o Tobie."
|
||||||
|
|
||||||
msgid "Your comment"
|
msgid "Your comment"
|
||||||
msgstr "Twój komentarz"
|
msgstr "Twój komentarz"
|
||||||
|
@ -318,9 +314,8 @@ msgid "We need an email or a username to identify you"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Potrzebujemy nazwy użytkownika lub adresu e-mail, aby Cię zidentyfikować"
|
"Potrzebujemy nazwy użytkownika lub adresu e-mail, aby Cię zidentyfikować"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Your password can't be empty"
|
msgid "Your password can't be empty"
|
||||||
msgstr "Twój komentarz nie może być pusty"
|
msgstr "Twoje hasło nie może być puste"
|
||||||
|
|
||||||
msgid "Passwords are not matching"
|
msgid "Passwords are not matching"
|
||||||
msgstr "Hasła nie pasują do siebie"
|
msgstr "Hasła nie pasują do siebie"
|
||||||
|
@ -361,20 +356,19 @@ msgid "Next page"
|
||||||
msgstr "Następna strona"
|
msgstr "Następna strona"
|
||||||
|
|
||||||
msgid "{{ user }} mentioned you."
|
msgid "{{ user }} mentioned you."
|
||||||
msgstr "{{ user }} wspomniał o Tobie."
|
msgstr "{{ user }} wspomniał(a) o Tobie."
|
||||||
|
|
||||||
msgid "{{ user }} commented your article."
|
msgid "{{ user }} commented your article."
|
||||||
msgstr "{{ user }} skomentował Twój artykuł."
|
msgstr "{{ user }} skomentował(a) Twój artykuł."
|
||||||
|
|
||||||
msgid "{{ user }} is now following you."
|
msgid "{{ user }} is now following you."
|
||||||
msgstr "{{ user }} zaczął Cię obserwować."
|
msgstr "{{ user }} zaczął(-ęła) Cię obserwować."
|
||||||
|
|
||||||
msgid "{{ user }} liked your article."
|
msgid "{{ user }} liked your article."
|
||||||
msgstr "{{ user }} polubił Twój artykuł."
|
msgstr "{{ user }} polubił(a) Twój artykuł."
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "{{ user }} boosted your article."
|
msgid "{{ user }} boosted your article."
|
||||||
msgstr "{{ user }} skomentował Twój artykuł."
|
msgstr "{{ user }} podbił(a) Twój artykuł."
|
||||||
|
|
||||||
msgid "Source code"
|
msgid "Source code"
|
||||||
msgstr "Kod źródłowy"
|
msgstr "Kod źródłowy"
|
||||||
|
@ -441,7 +435,7 @@ msgid "people"
|
||||||
msgstr "osób"
|
msgstr "osób"
|
||||||
|
|
||||||
msgid "Who wrote"
|
msgid "Who wrote"
|
||||||
msgstr "Którzy napisali"
|
msgstr "Które napisały"
|
||||||
|
|
||||||
msgid "articles"
|
msgid "articles"
|
||||||
msgstr "artykuły"
|
msgstr "artykuły"
|
||||||
|
@ -449,79 +443,83 @@ msgstr "artykuły"
|
||||||
msgid "Read the detailed rules"
|
msgid "Read the detailed rules"
|
||||||
msgstr "Przeczytaj szczegółowe zasady"
|
msgstr "Przeczytaj szczegółowe zasady"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Delete this article"
|
msgid "Delete this article"
|
||||||
msgstr "Najnowsze artykuły"
|
msgstr "Usuń ten artykuł"
|
||||||
|
|
||||||
msgid "And connected to"
|
msgid "And connected to"
|
||||||
msgstr ""
|
msgstr "Połączony z"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "other instances"
|
msgid "other instances"
|
||||||
msgstr "O tej instancji"
|
msgstr "innych instancji"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Administred by"
|
msgid "Administred by"
|
||||||
msgstr "Administracja"
|
msgstr "Administrowany przez"
|
||||||
|
|
||||||
msgid "Runs Plume {{ version }}"
|
msgid "Runs Plume {{ version }}"
|
||||||
msgstr ""
|
msgstr "Działa na Plume {{ version }}"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Your media"
|
msgid "Your media"
|
||||||
msgstr "Twój komentarz"
|
msgstr "Twoja zawartość multimedialna"
|
||||||
|
|
||||||
msgid "Go to your gallery"
|
msgid "Go to your gallery"
|
||||||
msgstr ""
|
msgstr "Przejdź do swojej galerii"
|
||||||
|
|
||||||
msgid "{{ name}}'s avatar'"
|
msgid "{{ name}}'s avatar'"
|
||||||
msgstr ""
|
msgstr "Awatar {{name}}"
|
||||||
|
|
||||||
msgid "Media details"
|
msgid "Media details"
|
||||||
msgstr ""
|
msgstr "Szczegóły zawartości multimedialnej"
|
||||||
|
|
||||||
msgid "Go back to the gallery"
|
msgid "Go back to the gallery"
|
||||||
msgstr ""
|
msgstr "Powróć do galerii"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Markdown code"
|
msgid "Markdown code"
|
||||||
msgstr "Markdown jest obsługiwany"
|
msgstr "Kod Markdown"
|
||||||
|
|
||||||
msgid "Copy it in your articles to insert this media."
|
msgid "Copy it in your articles to insert this media."
|
||||||
msgstr ""
|
msgstr "Skopiuj do swoich artykułów, aby wstawić tę zawartość multimedialną."
|
||||||
|
|
||||||
msgid "Use as avatar"
|
msgid "Use as avatar"
|
||||||
msgstr ""
|
msgstr "Użyj jako awataru"
|
||||||
|
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr "Usuń"
|
||||||
|
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr ""
|
msgstr "Wyślij"
|
||||||
|
|
||||||
msgid "You don't have any media yet."
|
msgid "You don't have any media yet."
|
||||||
msgstr ""
|
msgstr "Nie masz żadnej zawartości multimedialnej."
|
||||||
|
|
||||||
msgid "Media upload"
|
msgid "Media upload"
|
||||||
msgstr ""
|
msgstr "Wysyłanie zawartości multimedialnej"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Szczegółowy opis"
|
msgstr "Opis"
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
msgid "Content warning"
|
msgid "Content warning"
|
||||||
msgstr "Zawartość"
|
msgstr "Ostrzeżenie o zawartości"
|
||||||
|
|
||||||
msgid "File"
|
msgid "File"
|
||||||
msgstr ""
|
msgstr "Plik"
|
||||||
|
|
||||||
msgid "Send"
|
msgid "Send"
|
||||||
msgstr ""
|
msgstr "Wyślij"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Sorry, but registrations are closed on this instance. Try to find another one"
|
"Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Przepraszamy, rejestracja jest zamknięta na tej instancji. Spróbuj znaleźć "
|
||||||
|
"inną"
|
||||||
|
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr "Podtytuł"
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr "Zaloguj się aby polubić"
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr "Zaloguj się aby podbić"
|
||||||
|
|
||||||
#~ msgid "One reshare"
|
#~ msgid "One reshare"
|
||||||
#~ msgid_plural "{{ count }} reshares"
|
#~ msgid_plural "{{ count }} reshares"
|
||||||
|
|
|
@ -486,3 +486,12 @@ msgstr ""
|
||||||
|
|
||||||
msgid "Sorry, but registrations are closed on this instance. Try to find another one"
|
msgid "Sorry, but registrations are closed on this instance. Try to find another one"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Subtitle"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to like"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Login to boost"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -55,6 +55,10 @@ pub trait Inbox {
|
||||||
"Announce" => {
|
"Announce" => {
|
||||||
Reshare::delete_id(act.undo_props.object_object::<Announce>()?.object_props.id_string()?, conn);
|
Reshare::delete_id(act.undo_props.object_object::<Announce>()?.object_props.id_string()?, conn);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
},
|
||||||
|
"Follow" => {
|
||||||
|
Follow::delete_id(act.undo_props.object_object::<Like>()?.object_props.id_string()?, conn);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(InboxError::CantUndo)?
|
_ => Err(InboxError::CantUndo)?
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,8 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option<User>
|
||||||
"date": &post.creation_date.timestamp(),
|
"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, &vec![]))),
|
"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.clone().map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()),
|
"user_fqn": user.clone().map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()),
|
||||||
"is_author": user.map(|u| post.get_authors(&*conn).into_iter().any(|a| u.id == a.id)).unwrap_or(false)
|
"is_author": user.clone().map(|u| post.get_authors(&*conn).into_iter().any(|a| u.id == a.id)).unwrap_or(false),
|
||||||
|
"is_following": user.map(|u| u.is_following(&*conn, post.get_authors(&*conn)[0].id)).unwrap_or(false)
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -98,6 +99,7 @@ fn new(blog: String, user: User, conn: DbConn) -> Template {
|
||||||
struct NewPostForm {
|
struct NewPostForm {
|
||||||
#[validate(custom(function = "valid_slug", message = "Invalid title"))]
|
#[validate(custom(function = "valid_slug", message = "Invalid title"))]
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
pub subtitle: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub license: String
|
pub license: String
|
||||||
}
|
}
|
||||||
|
@ -150,7 +152,8 @@ fn create(blog_name: String, data: LenientForm<NewPostForm>, user: User, conn: D
|
||||||
Instance::get_local(&*conn).map(|i| i.default_license).unwrap_or(String::from("CC-0"))
|
Instance::get_local(&*conn).map(|i| i.default_license).unwrap_or(String::from("CC-0"))
|
||||||
},
|
},
|
||||||
ap_url: "".to_string(),
|
ap_url: "".to_string(),
|
||||||
creation_date: None
|
creation_date: None,
|
||||||
|
subtitle: form.subtitle.clone()
|
||||||
});
|
});
|
||||||
let post = post.update_ap_url(&*conn);
|
let post = post.update_ap_url(&*conn);
|
||||||
PostAuthor::insert(&*conn, NewPostAuthor {
|
PostAuthor::insert(&*conn, NewPostAuthor {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use activitypub::{
|
use activitypub::{
|
||||||
activity::{Create, Follow},
|
activity::Create,
|
||||||
collection::OrderedCollection,
|
collection::OrderedCollection,
|
||||||
object::Article
|
object::Article
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@ use workerpool::thunk::*;
|
||||||
|
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
ActivityStream, broadcast, Id, IntoId, ApRequest,
|
ActivityStream, broadcast, Id, IntoId, ApRequest,
|
||||||
inbox::{FromActivity, Notify}
|
inbox::{FromActivity, Notify, Deletable}
|
||||||
};
|
};
|
||||||
use plume_common::utils;
|
use plume_common::utils;
|
||||||
use plume_models::{
|
use plume_models::{
|
||||||
|
@ -71,7 +71,8 @@ fn details(name: String, conn: DbConn, account: Option<User>, worker: Worker, fe
|
||||||
.unwrap_or_else(|| User::fetch_from_url(&*fecth_followers_conn, user_id).expect("Couldn't fetch follower"));
|
.unwrap_or_else(|| User::fetch_from_url(&*fecth_followers_conn, user_id).expect("Couldn't fetch follower"));
|
||||||
follows::Follow::insert(&*fecth_followers_conn, follows::NewFollow {
|
follows::Follow::insert(&*fecth_followers_conn, follows::NewFollow {
|
||||||
follower_id: follower.id,
|
follower_id: follower.id,
|
||||||
following_id: user_clone.id
|
following_id: user_clone.id,
|
||||||
|
ap_url: format!("{}/follow/{}", follower.ap_url, user_clone.ap_url),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -116,20 +117,20 @@ fn dashboard_auth() -> Flash<Redirect> {
|
||||||
#[get("/@/<name>/follow")]
|
#[get("/@/<name>/follow")]
|
||||||
fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Redirect {
|
fn follow(name: String, conn: DbConn, user: User, worker: Worker) -> Redirect {
|
||||||
let target = User::find_by_fqn(&*conn, name.clone()).unwrap();
|
let target = User::find_by_fqn(&*conn, name.clone()).unwrap();
|
||||||
let f = follows::Follow::insert(&*conn, follows::NewFollow {
|
if let Some(follow) = follows::Follow::find(&*conn, user.id, target.id) {
|
||||||
follower_id: user.id,
|
let delete_act = follow.delete(&*conn);
|
||||||
following_id: target.id
|
worker.execute(Thunk::of(move || broadcast(&user, delete_act, vec![target])));
|
||||||
});
|
} else {
|
||||||
f.notify(&*conn);
|
let f = follows::Follow::insert(&*conn, follows::NewFollow {
|
||||||
|
follower_id: user.id,
|
||||||
|
following_id: target.id,
|
||||||
|
ap_url: format!("{}/follow/{}", user.ap_url, target.ap_url),
|
||||||
|
});
|
||||||
|
f.notify(&*conn);
|
||||||
|
|
||||||
let mut act = Follow::default();
|
let act = f.into_activity(&*conn);
|
||||||
act.follow_props.set_actor_link::<Id>(user.clone().into_id()).unwrap();
|
worker.execute(Thunk::of(move || broadcast(&user, act, vec![target])));
|
||||||
act.follow_props.set_object_object(user.into_activity(&*conn)).unwrap();
|
}
|
||||||
act.object_props.set_id_string(format!("{}/follow/{}", user.ap_url, target.ap_url)).unwrap();
|
|
||||||
act.object_props.set_to_link(target.clone().into_id()).expect("New Follow error while setting 'to'");
|
|
||||||
act.object_props.set_cc_link_vec::<Id>(vec![]).expect("New Follow error while setting 'cc'");
|
|
||||||
|
|
||||||
worker.execute(Thunk::of(move || broadcast(&user, act, vec![target])));
|
|
||||||
Redirect::to(uri!(details: name = name))
|
Redirect::to(uri!(details: name = name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,9 +192,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
article img {
|
article img {
|
||||||
|
display: block;
|
||||||
|
margin: 3em auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article pre {
|
||||||
|
padding: 1em;
|
||||||
|
background: #DADADA;
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Article.Meta */
|
/* Article.Meta */
|
||||||
|
|
||||||
main .article-meta, main .article-meta button {
|
main .article-meta, main .article-meta button {
|
||||||
|
@ -235,8 +244,8 @@
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
main .article-meta .likes button,
|
main .article-meta .likes .action,
|
||||||
main .article-meta .reshares button {
|
main .article-meta .reshares .action {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -246,15 +255,16 @@
|
||||||
background: none;
|
background: none;
|
||||||
color: #242424;
|
color: #242424;
|
||||||
border: none;
|
border: none;
|
||||||
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
main .article-meta .likes > p,
|
main .article-meta .likes > p,
|
||||||
main .article-meta .likes button:hover { color: #E92F2F; }
|
main .article-meta .likes .action:hover { color: #E92F2F; }
|
||||||
main .article-meta .reshares > p,
|
main .article-meta .reshares > p,
|
||||||
main .article-meta .reshares button:hover { color: #7765E3; }
|
main .article-meta .reshares .action:hover { color: #7765E3; }
|
||||||
|
|
||||||
main .article-meta .likes button svg.feather,
|
main .article-meta .likes .action svg.feather,
|
||||||
main .article-meta .reshares button i {
|
main .article-meta .reshares .action i {
|
||||||
transition: background 0.1s ease-in;
|
transition: background 0.1s ease-in;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -267,39 +277,39 @@
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
main .article-meta .likes button svg.feather {
|
main .article-meta .likes .action svg.feather {
|
||||||
padding: 0.7em;
|
padding: 0.7em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: #E92F2F;
|
color: #E92F2F;
|
||||||
fill: none;
|
fill: none;
|
||||||
border: solid #E92F2F thin;
|
border: solid #E92F2F thin;
|
||||||
}
|
}
|
||||||
main .article-meta .likes button:hover svg.feather {
|
main .article-meta .likes .action:hover svg.feather {
|
||||||
background: rgba(233, 47, 47, 0.15);
|
background: rgba(233, 47, 47, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
main .article-meta .reshares button i {
|
main .article-meta .reshares .action i {
|
||||||
color: #7765E3;
|
color: #7765E3;
|
||||||
border: solid #7765E3 thin;
|
border: solid #7765E3 thin;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
main .article-meta .reshares button:hover i {
|
main .article-meta .reshares .action:hover i {
|
||||||
background: rgba(119, 101, 227, 0.15);
|
background: rgba(119, 101, 227, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
main .article-meta .likes button.liked svg.feather { background: #E92F2F; fill: currentColor; }
|
main .article-meta .likes .action.liked svg.feather { background: #E92F2F; fill: currentColor; }
|
||||||
main .article-meta .likes button.liked:hover svg.feather {
|
main .article-meta .likes .action.liked:hover svg.feather {
|
||||||
background: rgba(233, 47, 47, 0.25);
|
background: rgba(233, 47, 47, 0.25);
|
||||||
color: #E92F2F;
|
color: #E92F2F;
|
||||||
}
|
}
|
||||||
main .article-meta .reshares button.reshared i { background: #7765E3; }
|
main .article-meta .reshares .action.reshared i { background: #7765E3; }
|
||||||
main .article-meta .reshares button.reshared:hover i {
|
main .article-meta .reshares .action.reshared:hover i {
|
||||||
background: rgba(119, 101, 227, 0.25);
|
background: rgba(119, 101, 227, 0.25);
|
||||||
color: #7765E3;
|
color: #7765E3;
|
||||||
}
|
}
|
||||||
|
|
||||||
main .article-meta .likes button.liked svg.feather,
|
main .article-meta .likes .action.liked svg.feather,
|
||||||
main .article-meta .reshares button.reshared i {
|
main .article-meta .reshares .action.reshared i {
|
||||||
color: #F4F4F4;
|
color: #F4F4F4;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
@ -796,5 +806,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar.padded {
|
.avatar.padded {
|
||||||
margin-right: 1em;
|
margin-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<span class="mobile-label">{{ "Notifications" | _ }}</span>
|
<span class="mobile-label">{{ "Notifications" | _ }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="/logout">
|
<a href="/logout">
|
||||||
<i class="icon icon-log-out aria-label="{{ "Log Out" | _ }}"></i>
|
<i class="icon icon-log-out" aria-label="{{ "Log Out" | _ }}"></i>
|
||||||
<span class="mobile-label">{{ "Log Out" | _ }}</span>
|
<span class="mobile-label">{{ "Log Out" | _ }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="/me">
|
<a href="/me">
|
||||||
|
|
|
@ -2,7 +2,13 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3><a href="{{ article.url }}">{{ article.post.title }}</a></h3>
|
<h3><a href="{{ article.url }}">{{ article.post.title }}</a></h3>
|
||||||
<main
|
<main
|
||||||
<p>{{ article.post.content | safe | striptags | truncate(length=200) }}</p>
|
<p>
|
||||||
|
{% if article.post.subtitle | length > 0 %}
|
||||||
|
{{ article.post.subtitle }}
|
||||||
|
{% else %}
|
||||||
|
{{ article.post.content | safe | striptags | truncate(length=200) }}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
</main>
|
</main>
|
||||||
<p class="author">
|
<p class="author">
|
||||||
{{ "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" | _(
|
{{ "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" | _(
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 class="article">{{ article.post.title }}</h1>
|
<h1 class="article">{{ article.post.title }}</h1>
|
||||||
|
<h2 class="article">{{ article.post.subtitle }}</h2>
|
||||||
<p class="article-info">
|
<p class="article-info">
|
||||||
<span class="author">{{ "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" | _(
|
<span class="author">{{ "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" | _(
|
||||||
link_1='<a href="/@/',
|
link_1='<a href="/@/',
|
||||||
|
@ -33,6 +34,20 @@
|
||||||
|
|
||||||
<div class="article-meta">
|
<div class="article-meta">
|
||||||
<p>{{ "This article is under the {{ license }} license." | _(license=article.post.license) }}</p>
|
<p>{{ "This article is under the {{ license }} license." | _(license=article.post.license) }}</p>
|
||||||
|
<div class="flex">
|
||||||
|
<img src="{{ author.avatar }}" alt="{{ author.name }}" class="avatar medium padded">
|
||||||
|
<div class="grow">
|
||||||
|
<h2><a href="/@/{{ author.fqn }}">{{ author.name }}</a></h2>
|
||||||
|
<p>{{ author.summary | safe }}</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/@/{{ author.fqn }}/follow" class="button">
|
||||||
|
{% if is_following %}
|
||||||
|
{{ "Unfollow" | _ }}
|
||||||
|
{% else %}
|
||||||
|
{{ "Follow" | _ }}
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if account %}
|
{% if account %}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
@ -40,23 +55,34 @@
|
||||||
<p aria-label="{{ "{{ count }} likes" | _n(singular="One like", count=n_likes) }}" title="{{ "{{ count }} likes" | _n(singular="One like", count=n_likes) }}">{{ n_likes }}</p>
|
<p aria-label="{{ "{{ count }} likes" | _n(singular="One like", count=n_likes) }}" title="{{ "{{ count }} likes" | _n(singular="One like", count=n_likes) }}">{{ n_likes }}</p>
|
||||||
|
|
||||||
{% if has_liked %}
|
{% if has_liked %}
|
||||||
<button type="submit" class="liked">{{ macros::feather(name="heart") }}{{ "I don't like this anymore" | _ }}</button>
|
<button type="submit" class="action liked">{{ macros::feather(name="heart") }}{{ "I don't like this anymore" | _ }}</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="submit">{{ macros::feather(name="heart") }}{{ "Add yours" | _ }}</button>
|
<button type="submit" class="action">{{ macros::feather(name="heart") }}{{ "Add yours" | _ }}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
<form class="reshares" action="{{ article.url }}reshare" method="POST">
|
<form class="reshares" action="{{ article.url }}reshare" method="POST">
|
||||||
<p aria-label="{{ "{{ count }} Boosts" | _n(singular="One Boost", count=n_reshares) }}" title="{{ "{{ count }} Boosts" | _n(singular="One Boost", count=n_reshares) }}">{{ n_reshares }}</p>
|
<p aria-label="{{ "{{ count }} Boosts" | _n(singular="One Boost", count=n_reshares) }}" title="{{ "{{ count }} Boosts" | _n(singular="One Boost", count=n_reshares) }}">{{ n_reshares }}</p>
|
||||||
|
|
||||||
{% if has_reshared %}
|
{% if has_reshared %}
|
||||||
<button type="submit" class="reshared"><i class="icon icon-repeat"></i>{{ "I don't want to boost this anymore" | _ }}</button>
|
<button type="submit" class="action reshared"><i class="icon icon-repeat"></i>{{ "I don't want to boost this anymore" | _ }}</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="submit"><i class="icon icon-repeat"></i>{{ "Boost" | _ }}</button>
|
<button type="submit" class="action"><i class="icon icon-repeat"></i>{{ "Boost" | _ }}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="center">{{ "Login or use your Fediverse account to interact with this article" | _ }}</p>
|
<p class="center">{{ "Login or use your Fediverse account to interact with this article" | _ }}</p>
|
||||||
|
<div class="actions">
|
||||||
|
<div class="likes">
|
||||||
|
<p aria-label="{{ "{{ count }} likes" | _n(singular="One like", count=n_likes) }}" title="{{ "{{ count }} likes" | _n(singular="One like", count=n_likes) }}">{{ n_likes }}</p>
|
||||||
|
<a href="/login?m=Login%20to%20like" class="action">{{ macros::feather(name="heart") }}{{ "Add yours" | _ }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="reshares">
|
||||||
|
<p aria-label="{{ "{{ count }} Boosts" | _n(singular="One Boost", count=n_reshares) }}" title="{{ "{{ count }} Boosts" | _n(singular="One Boost", count=n_reshares) }}">{{ n_reshares }}</p>
|
||||||
|
<a href="/login?m=Login%20to%20boost" class="action"><i class="icon icon-repeat"></i>{{ "Boost" | _ }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="comments">
|
<div class="comments">
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<h1>{{ "Create a post" | _ }}</h1>
|
<h1>{{ "Create a post" | _ }}</h1>
|
||||||
<form class="new-post" method="post">
|
<form class="new-post" method="post">
|
||||||
{{ macros::input(name="title", label="Title", errors=errors, form=form, props="required") }}
|
{{ macros::input(name="title", label="Title", errors=errors, form=form, props="required") }}
|
||||||
|
{{ macros::input(name="subtitle", label="Subtitle", errors=errors, form=form, optional=true) }}
|
||||||
|
|
||||||
{% if errors is defined and errors.content %}
|
{% if errors is defined and errors.content %}
|
||||||
{% for err in errors.content %}
|
{% for err in errors.content %}
|
||||||
|
|
Loading…
Reference in a new issue