From 705e74f4b9cf8a9558ca052ff4ea620d546946bc Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 24 Jul 2020 13:36:43 +0200 Subject: [PATCH 01/25] Federate sticky flag (ref #1018) --- server/src/apub/post.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index 39e4faf34..f71f49b59 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -15,8 +15,7 @@ use crate::{ }, blocking, routes::DbPoolParam, - DbPool, - LemmyError, + DbPool, LemmyError, }; use activitystreams_ext::Ext1; use activitystreams_new::{ From 678e1fa9273d77360086701180b97a3f72602191 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Fri, 24 Jul 2020 17:07:33 +0200 Subject: [PATCH 02/25] Add community outbox (ref #1018) --- server/lemmy_db/src/post.rs | 3 + server/src/apub/community.rs | 36 ++++++++- server/src/apub/fetcher.rs | 131 ++++++++++++++++++++------------ server/src/apub/mod.rs | 8 +- server/src/apub/post.rs | 3 +- server/src/apub/user.rs | 4 +- server/src/routes/federation.rs | 9 +-- 7 files changed, 129 insertions(+), 65 deletions(-) diff --git a/server/lemmy_db/src/post.rs b/server/lemmy_db/src/post.rs index d46677897..5eb9a4723 100644 --- a/server/lemmy_db/src/post.rs +++ b/server/lemmy_db/src/post.rs @@ -76,6 +76,9 @@ impl Post { use crate::schema::post::dsl::*; post .filter(community_id.eq(the_community_id)) + .then_order_by(published.desc()) + .then_order_by(stickied.desc()) + .limit(20) .load::(conn) } diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index 112b6e851..584ef310c 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -31,7 +31,7 @@ use activitystreams_new::{ }, actor::{kind::GroupType, ApActor, Endpoints, Group}, base::{AnyBase, BaseExt}, - collection::UnorderedCollection, + collection::{OrderedCollection, UnorderedCollection}, context, object::Tombstone, prelude::*, @@ -43,6 +43,7 @@ use lemmy_db::{ community::{Community, CommunityForm}, community_view::{CommunityFollowerView, CommunityModeratorView}, naive_now, + post::Post, user::User_, }; use lemmy_utils::convert_datetime; @@ -88,10 +89,10 @@ impl ToApub for Community { group.set_content(d); } - let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, group); + let mut ap_actor = ApActor::new(self.get_inbox_url()?, group); ap_actor .set_preferred_username(self.title.to_owned()) - .set_outbox(self.get_outbox_url().parse()?) + .set_outbox(self.get_outbox_url()?) .set_followers(self.get_followers_url().parse()?) .set_following(self.get_following_url().parse()?) .set_liked(self.get_liked_url().parse()?) @@ -411,6 +412,35 @@ pub async fn get_apub_community_followers( Ok(create_apub_response(&collection)) } +pub async fn get_apub_community_outbox( + info: web::Path, + db: DbPoolParam, +) -> Result, LemmyError> { + let community = blocking(&db, move |conn| { + Community::read_from_name(&conn, &info.community_name) + }) + .await??; + + let community_id = community.id; + let posts = blocking(&db, move |conn| { + Post::list_for_community(conn, community_id) + }) + .await??; + + let mut pages: Vec = vec![]; + for p in posts { + pages.push(p.to_apub(&db).await?.into_any_base()?); + } + + let len = pages.len(); + let mut collection = OrderedCollection::new(pages); + collection + .set_context(context()) + .set_id(community.get_outbox_url()?) + .set_total_items(len as u64); + Ok(create_apub_response(&collection)) +} + pub async fn do_announce( activity: AnyBase, community: &Community, diff --git a/server/src/apub/fetcher.rs b/server/src/apub/fetcher.rs index c10426d14..e2d505df7 100644 --- a/server/src/apub/fetcher.rs +++ b/server/src/apub/fetcher.rs @@ -15,7 +15,7 @@ use crate::{ DbPool, LemmyError, }; -use activitystreams_new::{base::BaseExt, object::Note, prelude::*}; +use activitystreams_new::{base::BaseExt, collection::OrderedCollection, object::Note, prelude::*}; use actix_web::client::Client; use chrono::NaiveDateTime; use diesel::{result::Error::NotFound, PgConnection}; @@ -40,6 +40,7 @@ use std::{fmt::Debug, time::Duration}; use url::Url; static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60; +static ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG: i64 = 10; // Fetch nodeinfo metadata from a remote instance. async fn _fetch_node_info(client: &Client, domain: &str) -> Result { @@ -257,12 +258,13 @@ pub async fn get_or_fetch_and_upsert_user( /// TODO it won't pick up new avatars, summaries etc until a day after. /// Actors need an "update" activity pushed to other servers to fix this. fn should_refetch_actor(last_refreshed: NaiveDateTime) -> bool { - if cfg!(debug_assertions) { - true + let update_interval = if cfg!(debug_assertions) { + // avoid infinite loop when fetching community outbox + chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG) } else { - let update_interval = chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS); - last_refreshed.lt(&(naive_now() - update_interval)) - } + chrono::Duration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS) + }; + last_refreshed.lt(&(naive_now() - update_interval)) } /// Check if a remote community exists, create if not found, if its too old update it.Fetch a community, insert/update it in the database and return the community. @@ -280,59 +282,88 @@ pub async fn get_or_fetch_and_upsert_community( match community { Ok(c) if !c.local && should_refetch_actor(c.last_refreshed_at) => { debug!("Fetching and updating from remote community: {}", apub_id); - let group = fetch_remote_object::(client, apub_id).await?; - - let mut cf = CommunityForm::from_apub(&group, client, pool).await?; - cf.last_refreshed_at = Some(naive_now()); - let community = blocking(pool, move |conn| Community::update(conn, c.id, &cf)).await??; - - Ok(community) + fetch_remote_community(apub_id, client, pool, Some(c.id)).await } Ok(c) => Ok(c), Err(NotFound {}) => { debug!("Fetching and creating remote community: {}", apub_id); - let group = fetch_remote_object::(client, apub_id).await?; - - let cf = CommunityForm::from_apub(&group, client, pool).await?; - let community = blocking(pool, move |conn| Community::create(conn, &cf)).await??; - - // Also add the community moderators too - let attributed_to = group.inner.attributed_to().unwrap(); - let creator_and_moderator_uris: Vec<&Url> = attributed_to - .as_many() - .unwrap() - .iter() - .map(|a| a.as_xsd_any_uri().unwrap()) - .collect(); - - let mut creator_and_moderators = Vec::new(); - - for uri in creator_and_moderator_uris { - let c_or_m = get_or_fetch_and_upsert_user(uri, client, pool).await?; - - creator_and_moderators.push(c_or_m); - } - - let community_id = community.id; - blocking(pool, move |conn| { - for mod_ in creator_and_moderators { - let community_moderator_form = CommunityModeratorForm { - community_id, - user_id: mod_.id, - }; - - CommunityModerator::join(conn, &community_moderator_form)?; - } - Ok(()) as Result<(), LemmyError> - }) - .await??; - - Ok(community) + fetch_remote_community(apub_id, client, pool, None).await } Err(e) => Err(e.into()), } } +async fn fetch_remote_community( + apub_id: &Url, + client: &Client, + pool: &DbPool, + community_id: Option, +) -> Result { + let group = fetch_remote_object::(client, apub_id).await?; + + let cf = CommunityForm::from_apub(&group, client, pool).await?; + let community = blocking(pool, move |conn| { + if let Some(cid) = community_id { + Community::update(conn, cid, &cf) + } else { + Community::create(conn, &cf) + } + }) + .await??; + + // Also add the community moderators too + let attributed_to = group.inner.attributed_to().unwrap(); + let creator_and_moderator_uris: Vec<&Url> = attributed_to + .as_many() + .unwrap() + .iter() + .map(|a| a.as_xsd_any_uri().unwrap()) + .collect(); + + let mut creator_and_moderators = Vec::new(); + + for uri in creator_and_moderator_uris { + let c_or_m = get_or_fetch_and_upsert_user(uri, client, pool).await?; + + creator_and_moderators.push(c_or_m); + } + + // TODO: need to make this work to update mods of existing communities + if community_id.is_none() { + let community_id = community.id; + blocking(pool, move |conn| { + for mod_ in creator_and_moderators { + let community_moderator_form = CommunityModeratorForm { + community_id, + user_id: mod_.id, + }; + + CommunityModerator::join(conn, &community_moderator_form)?; + } + Ok(()) as Result<(), LemmyError> + }) + .await??; + } + + // fetch outbox (maybe make this conditional) + let outbox = + fetch_remote_object::(client, &community.get_outbox_url()?).await?; + let outbox_items = outbox.items().clone(); + for o in outbox_items.many().unwrap() { + let page = PageExt::from_any_base(o)?.unwrap(); + let post = PostForm::from_apub(&page, client, pool).await?; + let post_ap_id = post.ap_id.clone(); + // Check whether the post already exists in the local db + let existing = blocking(pool, move |conn| Post::read_from_apub_id(conn, &post_ap_id)).await?; + match existing { + Ok(e) => blocking(pool, move |conn| Post::update(conn, e.id, &post)).await??, + Err(_) => blocking(pool, move |conn| Post::create(conn, &post)).await??, + }; + } + + Ok(community) +} + fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result { let existing = Post::read_from_apub_id(conn, &post_form.ap_id); match existing { diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 28eb86ac2..feb1f30fc 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -280,8 +280,8 @@ pub trait ActorType { } // TODO move these to the db rows - fn get_inbox_url(&self) -> String { - format!("{}/inbox", &self.actor_id_str()) + fn get_inbox_url(&self) -> Result { + Url::parse(&format!("{}/inbox", &self.actor_id_str())) } // TODO: make this return `Result @@ -289,8 +289,8 @@ pub trait ActorType { get_shared_inbox(&self.actor_id().unwrap()) } - fn get_outbox_url(&self) -> String { - format!("{}/outbox", &self.actor_id_str()) + fn get_outbox_url(&self) -> Result { + Url::parse(&format!("{}/outbox", &self.actor_id_str())) } fn get_followers_url(&self) -> String { diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index f71f49b59..39e4faf34 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -15,7 +15,8 @@ use crate::{ }, blocking, routes::DbPoolParam, - DbPool, LemmyError, + DbPool, + LemmyError, }; use activitystreams_ext::Ext1; use activitystreams_new::{ diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs index 0e90941d6..463208603 100644 --- a/server/src/apub/user.rs +++ b/server/src/apub/user.rs @@ -63,9 +63,9 @@ impl ToApub for User_ { person.set_icon(image.into_any_base()?); } - let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, person); + let mut ap_actor = ApActor::new(self.get_inbox_url()?, person); ap_actor - .set_outbox(self.get_outbox_url().parse()?) + .set_outbox(self.get_outbox_url()?) .set_followers(self.get_followers_url().parse()?) .set_following(self.get_following_url().parse()?) .set_liked(self.get_liked_url().parse()?) diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index 93aaac1c1..2a0c81b23 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -28,11 +28,10 @@ pub fn config(cfg: &mut web::ServiceConfig) { "/c/{community_name}/followers", web::get().to(get_apub_community_followers), ) - // TODO This is only useful for history which we aren't doing right now - // .route( - // "/c/{community_name}/outbox", - // web::get().to(get_apub_community_outbox), - // ) + .route( + "/c/{community_name}/outbox", + web::get().to(get_apub_community_outbox), + ) .route("/u/{user_name}", web::get().to(get_apub_user_http)) .route("/post/{post_id}", web::get().to(get_apub_post)) .route("/comment/{comment_id}", web::get().to(get_apub_comment)), From d2074cb669b0bfb6a1f7721c531925dc759d55dc Mon Sep 17 00:00:00 2001 From: Lucy Date: Wed, 29 Jul 2020 22:06:20 +0000 Subject: [PATCH 03/25] Translated using Weblate (Irish) Currently translated at 100.0% (266 of 266 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/ga/ --- ui/translations/ga.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/translations/ga.json b/ui/translations/ga.json index c23484016..943fea62f 100644 --- a/ui/translations/ga.json +++ b/ui/translations/ga.json @@ -298,5 +298,7 @@ "user_already_exists": "Úsáideoir ann cheana.", "email_already_exists": "Tá ríomhphost ann cheana féin.", "couldnt_update_user": "Níorbh fhéidir an t-úsáideoir a nuashonrú.", - "time": "Am" + "time": "Am", + "subscript": "fo-script", + "superscript": "sár-script" } From 9ddc6659ec0617898d96fe0a924c50c81595bf4c Mon Sep 17 00:00:00 2001 From: Monday Tuesday Date: Wed, 29 Jul 2020 22:06:21 +0000 Subject: [PATCH 04/25] Translated using Weblate (Polish) Currently translated at 92.1% (245 of 266 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/pl/ --- ui/translations/pl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/translations/pl.json b/ui/translations/pl.json index ad2e9c1c4..4c737b3ff 100644 --- a/ui/translations/pl.json +++ b/ui/translations/pl.json @@ -262,5 +262,6 @@ "emoji_picker": "Wybór Emoji", "silver_sponsors": "Srebrni Sponsorzy to ci, którzy wpłacili co najmniej $40 na Lemmiego.", "select_a_community": "Wybierz społeczność", - "invalid_username": "Nieprawidłowa nazwa użytkownika." + "invalid_username": "Nieprawidłowa nazwa użytkownika.", + "invalid_community_name": "Niepoprawna nazwa." } From d14bc121c0aa19bcec2c85c5d0b581f00dcae84a Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 29 Jul 2020 22:29:41 -0400 Subject: [PATCH 05/25] Fixing language setting issue. Fixes #1041 --- ui/src/components/navbar.tsx | 3 ++- ui/src/components/user.tsx | 3 ++- ui/src/utils.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx index 2986ce7ea..6418bb222 100644 --- a/ui/src/components/navbar.tsx +++ b/ui/src/components/navbar.tsx @@ -30,6 +30,7 @@ import { messageToastify, md, setTheme, + getLanguage, } from '../utils'; import { i18n } from '../i18next'; @@ -435,7 +436,7 @@ export class Navbar extends Component { this.requestNotificationPermission(); this.fetchUnreads(); setTheme(data.my_user.theme, true); - i18n.changeLanguage(data.my_user.lang); + i18n.changeLanguage(getLanguage()); } this.state.isLoggedIn = true; } diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index e4d4439b1..d0dcdf55e 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -30,6 +30,7 @@ import { showAvatars, toast, setupTippy, + getLanguage, } from '../utils'; import { UserListing } from './user-listing'; import { SortSelect } from './sort-select'; @@ -877,7 +878,7 @@ export class User extends Component { handleUserSettingsLangChange(i: User, event: any) { i.state.userSettingsForm.lang = event.target.value; - i18n.changeLanguage(i.state.userSettingsForm.lang); + i18n.changeLanguage(getLanguage(i.state.userSettingsForm.lang)); i.setState(i.state); } diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 892516bff..fa7f12978 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -350,9 +350,9 @@ export function debounce( }; } -export function getLanguage(): string { +export function getLanguage(override: string): string { let user = UserService.Instance.user; - let lang = user && user.lang ? user.lang : 'browser'; + let lang = override || (user && user.lang ? user.lang : 'browser'); if (lang == 'browser') { return getBrowserLanguage(); From 25e61b276a93743b1ff1206a1612ce8bf62b4b02 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 29 Jul 2020 22:30:38 -0400 Subject: [PATCH 06/25] Version v0.7.34 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- docker/travis/docker_push.sh | 4 ++-- server/src/version.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index a99663bea..fcab30d70 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.7.33 +v0.7.34 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 414435f5d..0e38e00d8 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -12,7 +12,7 @@ services: restart: always lemmy: - image: dessalines/lemmy:v0.7.33 + image: dessalines/lemmy:v0.7.34 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/docker/travis/docker_push.sh b/docker/travis/docker_push.sh index d0c9e79ee..1c6bcbc8d 100644 --- a/docker/travis/docker_push.sh +++ b/docker/travis/docker_push.sh @@ -1,5 +1,5 @@ #!/bin/sh echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin docker tag dessalines/lemmy:travis \ - dessalines/lemmy:v0.7.33 -docker push dessalines/lemmy:v0.7.33 + dessalines/lemmy:v0.7.34 +docker push dessalines/lemmy:v0.7.34 diff --git a/server/src/version.rs b/server/src/version.rs index 6dd04350b..f316aca58 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.7.33"; +pub const VERSION: &str = "v0.7.34"; From c24a619594f4a6c611f4d8da4eb77ed41601abf2 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 29 Jul 2020 22:35:08 -0400 Subject: [PATCH 07/25] Minor syntax fix. --- ui/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/utils.ts b/ui/src/utils.ts index fa7f12978..55a0777ab 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -350,7 +350,7 @@ export function debounce( }; } -export function getLanguage(override: string): string { +export function getLanguage(override?: string): string { let user = UserService.Instance.user; let lang = override || (user && user.lang ? user.lang : 'browser'); From dc4ac6345c31926e2633465b9f05ebc0b5759eaf Mon Sep 17 00:00:00 2001 From: Dessalines Date: Wed, 29 Jul 2020 22:36:27 -0400 Subject: [PATCH 08/25] Version v0.7.35 --- ansible/VERSION | 2 +- docker/prod/docker-compose.yml | 2 +- docker/travis/docker_push.sh | 4 ++-- server/src/version.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ansible/VERSION b/ansible/VERSION index fcab30d70..d02286842 100644 --- a/ansible/VERSION +++ b/ansible/VERSION @@ -1 +1 @@ -v0.7.34 +v0.7.35 diff --git a/docker/prod/docker-compose.yml b/docker/prod/docker-compose.yml index 0e38e00d8..334d76f95 100644 --- a/docker/prod/docker-compose.yml +++ b/docker/prod/docker-compose.yml @@ -12,7 +12,7 @@ services: restart: always lemmy: - image: dessalines/lemmy:v0.7.34 + image: dessalines/lemmy:v0.7.35 ports: - "127.0.0.1:8536:8536" restart: always diff --git a/docker/travis/docker_push.sh b/docker/travis/docker_push.sh index 1c6bcbc8d..71999bfe1 100644 --- a/docker/travis/docker_push.sh +++ b/docker/travis/docker_push.sh @@ -1,5 +1,5 @@ #!/bin/sh echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin docker tag dessalines/lemmy:travis \ - dessalines/lemmy:v0.7.34 -docker push dessalines/lemmy:v0.7.34 + dessalines/lemmy:v0.7.35 +docker push dessalines/lemmy:v0.7.35 diff --git a/server/src/version.rs b/server/src/version.rs index f316aca58..6b06cf4eb 100644 --- a/server/src/version.rs +++ b/server/src/version.rs @@ -1 +1 @@ -pub const VERSION: &str = "v0.7.34"; +pub const VERSION: &str = "v0.7.35"; From e31f74c3ad354f4401e8ce0b0f65de8d1d8ebe41 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 30 Jul 2020 11:51:28 -0400 Subject: [PATCH 09/25] Add domain name change instructions to docs. (#1044) * Add domain name change instructions to docs. * Changing docker execs to docker-compose execs --- docs/src/administration_backup_and_restore.md | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/docs/src/administration_backup_and_restore.md b/docs/src/administration_backup_and_restore.md index fe97cf880..633c687fb 100644 --- a/docs/src/administration_backup_and_restore.md +++ b/docs/src/administration_backup_and_restore.md @@ -9,14 +9,14 @@ When using docker or ansible, there should be a `volumes` folder, which contains To incrementally backup the DB to an `.sql` file, you can run: ```bash -docker exec -t FOLDERNAME_postgres_1 pg_dumpall -c -U lemmy > lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql +docker-compose exec postgres pg_dumpall -c -U lemmy > lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql ``` ### A Sample backup script ```bash #!/bin/sh # DB Backup -ssh MY_USER@MY_IP "docker exec -t FOLDERNAME_postgres_1 pg_dumpall -c -U lemmy" > ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql +ssh MY_USER@MY_IP "docker-compose exec postgres pg_dumpall -c -U lemmy" > ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql # Volumes folder Backup rsync -avP -zz --rsync-path="sudo rsync" MY_USER@MY_IP:/LEMMY_LOCATION/volumes ~/BACKUP_LOCATION/FOLDERNAME @@ -37,6 +37,45 @@ cat db_dump.sql | docker exec -i FOLDERNAME_postgres_1 psql -U lemmy # restore docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "alter user lemmy with password 'bleh'" ``` +### Changing your domain name + +If you haven't federated yet, you can change your domain name in the DB. **Warning: do not do this after you've federated, or it will break federation.** + +Get into `psql` for your docker: + +`docker-compose exec postgres psql -U lemmy` + +``` +-- Post +update post set ap_id = replace (ap_id, 'old_domain', 'new_domain'); +update post set url = replace (url, 'old_domain', 'new_domain'); +update post set body = replace (body, 'old_domain', 'new_domain'); +update post set thumbnail_url = replace (thumbnail_url, 'old_domain', 'new_domain'); + +delete from post_aggregates_fast; +insert into post_aggregates_fast select * from post_aggregates_view; + +-- Comments +update comment set ap_id = replace (ap_id, 'old_domain', 'new_domain'); +update comment set content = replace (content, 'old_domain', 'new_domain'); + +delete from comment_aggregates_fast; +insert into comment_aggregates_fast select * from comment_aggregates_view; + +-- User +update user_ set actor_id = replace (actor_id, 'old_domain', 'new_domain'); +update user_ set avatar = replace (avatar, 'old_domain', 'new_domain'); + +delete from user_fast; +insert into user_fast select * from user_view; + +-- Community +update community set actor_id = replace (actor_id, 'old_domain', 'new_domain'); + +delete from community_aggregates_fast; +insert into community_aggregates_fast select * from community_aggregates_view; +``` + ## More resources - https://stackoverflow.com/questions/24718706/backup-restore-a-dockerized-postgresql-database From 90ae426e247665f9985e1c0740b185f1e513a835 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 30 Jul 2020 14:25:01 -0400 Subject: [PATCH 10/25] Some small fixes. --- ui/src/components/comment-form.tsx | 3 +++ ui/src/utils.ts | 11 +++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index 01222b27c..5597f58ee 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -144,6 +144,9 @@ export class CommentForm extends Component { // This only finishes this form, if the randomly generated form_id matches the one received if (this.state.commentForm.form_id == data.form_id) { this.setState({ finished: true }); + + // Necessary because it broke tribute for some reaso + this.setState({ finished: false }); } } } diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 55a0777ab..2ef1d070c 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -588,6 +588,9 @@ export function messageToastify( export function setupTribute(): Tribute { return new Tribute({ + noMatchTemplate: function () { + return ''; + }, collection: [ // Emojis { @@ -669,7 +672,7 @@ function userSearch(text: string, cb: any) { WebSocketService.Instance.search(form); - this.userSub = WebSocketService.Instance.subject.subscribe( + let userSub = WebSocketService.Instance.subject.subscribe( msg => { let res = wsJsonToRes(msg); if (res.op == UserOperation.Search) { @@ -683,7 +686,7 @@ function userSearch(text: string, cb: any) { }; }); cb(users); - this.userSub.unsubscribe(); + userSub.unsubscribe(); } }, err => console.error(err), @@ -706,7 +709,7 @@ function communitySearch(text: string, cb: any) { WebSocketService.Instance.search(form); - this.communitySub = WebSocketService.Instance.subject.subscribe( + let communitySub = WebSocketService.Instance.subject.subscribe( msg => { let res = wsJsonToRes(msg); if (res.op == UserOperation.Search) { @@ -720,7 +723,7 @@ function communitySearch(text: string, cb: any) { }; }); cb(communities); - this.communitySub.unsubscribe(); + communitySub.unsubscribe(); } }, err => console.error(err), From 1e75e6591ad99bcf1dffe4d7e33e8825ad5eb04c Mon Sep 17 00:00:00 2001 From: Monday Tuesday Date: Thu, 30 Jul 2020 21:37:32 +0000 Subject: [PATCH 11/25] Translated using Weblate (German) Currently translated at 100.0% (266 of 266 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/de/ Translated using Weblate (Polish) Currently translated at 92.1% (245 of 266 strings) Translation: Lemmy/lemmy Translate-URL: http://weblate.yerbamate.dev/projects/lemmy/lemmy/pl/ --- ui/translations/de.json | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/ui/translations/de.json b/ui/translations/de.json index 3199bc7ad..597a8bb92 100644 --- a/ui/translations/de.json +++ b/ui/translations/de.json @@ -14,7 +14,7 @@ "number_of_comments": "{{count}} Kommentar", "number_of_comments_plural": "{{count}} Kommentare", "remove_comment": "Kommentar löschen", - "communities": "Communitys", + "communities": "Communities", "users": "Benutzer", "create_a_community": "Eine Community anlegen", "create_community": "Community erstellen", @@ -168,7 +168,7 @@ "yes": "Ja", "no": "Nein", "powered_by": "Bereitgestellt durch", - "landing_0": "Lemmy ist ein <1>Link-Aggregator / Reddit Alternative im <2>Fediverse.<3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB). Federation in das ActivityPub Netzwerk ist geplant. <5>Dies ist eine <6>sehr frühe Beta Version, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7>Schlage neue Features vor oder melde Bugs <8>hier.<9>Gebaut mit <10>Rust, <11>Actix, <12>Inferno, <13>Typescript.", + "landing": "Lemmy ist ein <1>Link-Aggregator / Reddit Alternative im <2>Fediverse.<3>Es ist selbst-hostbar, hat live-updates von Kommentar-threads und ist winzig (<4>~80kB). Federation in das ActivityPub Netzwerk ist geplant. <5>Dies ist eine <6>sehr frühe Beta Version, und viele Features funktionieren zurzeit nicht richtig oder fehlen. <7>Schlage neue Features vor oder melde Bugs <8>hier.<9>Gebaut mit <10>Rust, <11>Actix, <12>Inferno, <13>Typescript. <14> <15>Vielen Dank an unsere Mitwirkenden: dessalines, Nutomic, asonix, zacanger, and iav.", "not_logged_in": "Nicht eingeloggt.", "community_ban": "Du wurdest von dieser Community gebannt.", "site_ban": "Du wurdest von dieser Seite gebannt", @@ -216,7 +216,7 @@ "messages": "Nachrichten", "old_password": "Letztes Passwort", "matrix_user_id": "Matrix Benutzer", - "private_message_disclaimer": "Achtung: Private Nachrichten sind in Lemmy nicht sicher. Bitte erstelle einen <1>Riot.im Account für sicheren Nachrichtenverkehr.", + "private_message_disclaimer": "Achtung: Private Nachrichten sind in Lemmy nicht verschlüsselt. Bitte erstelle einen<1>Element.io Account für sicheren Nachrichtenverkehr.", "send_notifications_to_email": "Sende Benachrichtigungen per Email", "downvotes_disabled": "Downvotes deaktiviert", "enable_downvotes": "Aktiviere Downvotes", @@ -256,5 +256,22 @@ "click_to_delete_picture": "Klicke, um das Bild zu löschen.", "picture_deleted": "Bild gelöscht.", "select_a_community": "Wähle eine Community aus", - "invalid_username": "Ungültiger Benutzername." + "invalid_username": "Ungültiger Benutzername.", + "bold": "fett", + "italic": "kursiv", + "subscript": "Tiefzeichen", + "superscript": "Hochzeichen", + "header": "Header", + "strikethrough": "durchgestrichen", + "quote": "Zitat", + "spoiler": "Spoiler", + "list": "Liste", + "not_a_moderator": "Kein Moderator.", + "invalid_url": "Ungültige URL.", + "must_login": "Du musst <1>eingeloggt oder registriert sein um zu Kommentieren.", + "no_password_reset": "Du kannst dein Passwort ohne E-Mail nicht zurücksetzen.", + "cake_day_info": "Heute ist {{ creator_name }}'s cake day!", + "invalid_post_title": "Ungültiger Post Titel", + "cake_day_title": "Cake day:", + "what_is": "Was ist" } From 1acb51105a3316ab52f7d65970839a766899ad14 Mon Sep 17 00:00:00 2001 From: Azriel Lector Date: Fri, 31 Jul 2020 09:08:13 +0800 Subject: [PATCH 12/25] Add user bios (#1043) * Add user bios * Version v0.7.35 * Add domain name change instructions to docs. (#1044) * Add domain name change instructions to docs. * Changing docker execs to docker-compose execs * Set maxLength to user bio and render as md * Fix bio updating after SaveUserSetting Co-authored-by: Dessalines Co-authored-by: Dessalines --- .gitignore | 1 + server/src/api/user.rs | 14 ++++++++- ui/src/components/markdown-textarea.tsx | 3 +- ui/src/components/user.tsx | 40 +++++++++++++++++++++++-- ui/src/interfaces.ts | 1 + ui/translations/en.json | 4 ++- 6 files changed, 57 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 6ae0ae193..c3a8bd70e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,6 @@ ui/src/translations # ide config .idea/ +.vscode/ target diff --git a/server/src/api/user.rs b/server/src/api/user.rs index c2b6955b5..f9a92cd39 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -97,6 +97,7 @@ pub struct SaveUserSettings { lang: String, avatar: Option, email: Option, + bio: Option, matrix_user_id: Option, new_password: Option, new_password_verify: Option, @@ -557,6 +558,17 @@ impl Perform for Oper { None => read_user.email, }; + let bio = match &data.bio { + Some(bio) => { + if bio.chars().count() <= 300 { + Some(bio.to_owned()) + } else { + return Err(APIError::err("bio_length_overflow").into()); + } + } + None => read_user.bio, + }; + let avatar = match &data.avatar { Some(avatar) => Some(avatar.to_owned()), None => read_user.avatar, @@ -613,7 +625,7 @@ impl Perform for Oper { show_avatars: data.show_avatars, send_notifications_to_email: data.send_notifications_to_email, actor_id: read_user.actor_id, - bio: read_user.bio, + bio, local: read_user.local, private_key: read_user.private_key, public_key: read_user.public_key, diff --git a/ui/src/components/markdown-textarea.tsx b/ui/src/components/markdown-textarea.tsx index 237ef9ff3..002d7c86b 100644 --- a/ui/src/components/markdown-textarea.tsx +++ b/ui/src/components/markdown-textarea.tsx @@ -21,6 +21,7 @@ interface MarkdownTextAreaProps { replyType?: boolean; focus?: boolean; disabled?: boolean; + maxLength?: number; onSubmit?(msg: { val: string; formId: string }): any; onContentChange?(val: string): any; onReplyCancel?(): any; @@ -121,7 +122,7 @@ export class MarkdownTextArea extends Component< required disabled={this.props.disabled} rows={2} - maxLength={10000} + maxLength={this.props.maxLength || 10000} /> {this.state.previewMode && (
{ show_avatars: null, send_notifications_to_email: null, auth: null, + bio: null, }, userSettingsLoading: null, deleteAccountLoading: null, @@ -149,7 +152,13 @@ export class User extends Component { this.handleUserSettingsListingTypeChange = this.handleUserSettingsListingTypeChange.bind( this ); + this.handleUserSettingsListingTypeChange = this.handleUserSettingsListingTypeChange.bind( + this + ); this.handlePageChange = this.handlePageChange.bind(this); + this.handleUserSettingsBioChange = this.handleUserSettingsBioChange.bind( + this + ); this.state.user_id = Number(this.props.match.params.id) || null; this.state.username = this.props.match.params.username; @@ -375,6 +384,12 @@ export class User extends Component { )} +
+
+
@@ -570,6 +585,18 @@ export class User extends Component { />
+
+ +
+ +
+