"eslint": "^7.18.0",
"eslint-plugin-jane": "^9.0.3",
"jest": "^26.6.3",
- "lemmy-js-client": "0.11.0-rc.1",
+ "lemmy-js-client": "0.11.0-rc.3",
"node-fetch": "^2.6.1",
"prettier": "^2.1.2",
"ts-jest": "^26.4.4",
expect(communityOne.community.published).toBe(
communityTwo.community.published
);
- expect(communityOne.creator.actor_id).toBe(communityTwo.creator.actor_id);
expect(communityOne.community.nsfw).toBe(communityTwo.community.nsfw);
expect(communityOne.community.removed).toBe(communityTwo.community.removed);
expect(communityOne.community.deleted).toBe(communityTwo.community.deleted);
beforeAll(async () => {
await setupLogins();
- let follow = await followBeta(alpha);
- recipient_id = follow.community_view.creator.id;
+ await followBeta(alpha);
+ recipient_id = 3;
});
afterAll(async () => {
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
expect(userOne.person.name).toBe(userTwo.person.name);
- expect(userOne.person.display_name).toBe(userTwo.person.display_name);
+ expect(userOne.person.preferred_username).toBe(userTwo.person.preferred_username);
expect(userOne.person.bio).toBe(userTwo.person.bio);
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
expect(userOne.person.avatar).toBe(userTwo.person.avatar);
lang: '',
avatar,
banner,
- display_name: 'user321',
+ preferred_username: 'user321',
show_avatars: false,
send_notifications_to_email: false,
bio,
dependencies:
language-subtag-registry "~0.3.2"
-lemmy-js-client@0.11.0-rc.1:
- version "0.11.0-rc.1"
- resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.0-rc.1.tgz#0031676be9fc787157a21dd3f5095dd1ee9e6a57"
- integrity sha512-dtpxe/hHTbYEv2WnfGkAieOB9jyKUVED+y4DosUp/FcaatjPcMTiKOvCdMNjlvvG/9GyclWEoyNitPEsvJwjmg==
+lemmy-js-client@0.11.0-rc.3:
+ version "0.11.0-rc.3"
+ resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.0-rc.3.tgz#dd4727ca4d16fe9593368725aacfd9e7a8d52548"
+ integrity sha512-16mgl+TS1+0UHiY+ZKPuqHfbrn93h8K8tJ+kKJ1pjT2WhG23o0B8dLahG1jDQPG+dkXpR6PZxYudMYjuO8WvjQ==
leven@^3.1.0:
version "3.1.0"
};
use lemmy_apub::{ActorType, CommunityType, UserType};
use lemmy_db_queries::{
- source::{
- comment::Comment_,
- community::{CommunityModerator_, Community_},
- post::Post_,
- },
+ source::{comment::Comment_, community::CommunityModerator_, post::Post_},
Bannable,
Crud,
Followable,
let data: &TransferCommunity = &self;
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
- let community_id = data.community_id;
- let read_community = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
- })
- .await??;
-
let site_creator_id = blocking(context.pool(), move |conn| {
Site::read(conn, 1).map(|s| s.creator_id)
})
let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
- // Making sure the creator, if an admin, is at the top
+ // Making sure the site creator, if an admin, is at the top
let creator_index = admins
.iter()
.position(|r| r.person.id == site_creator_id)
let creator_person = admins.remove(creator_index);
admins.insert(0, creator_person);
- // Make sure user is the creator, or an admin
- if local_user_view.person.id != read_community.creator_id
+ // Fetch the community mods
+ let community_id = data.community_id;
+ let mut community_mods = blocking(context.pool(), move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await??;
+
+ // Make sure transferrer is either the top community mod, or an admin
+ if local_user_view.person.id != community_mods[0].moderator.id
&& !admins
.iter()
.map(|a| a.person.id)
return Err(ApiError::err("not_an_admin").into());
}
- let community_id = data.community_id;
- let new_creator = data.person_id;
- let update = move |conn: &'_ _| Community::update_creator(conn, community_id, new_creator);
- if blocking(context.pool(), update).await?.is_err() {
- return Err(ApiError::err("couldnt_update_community").into());
- };
-
- // You also have to re-do the community_moderator table, reordering it.
- let community_id = data.community_id;
- let mut community_mods = blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- })
- .await??;
+ // You have to re-do the community_moderator table, reordering it.
+ // Add the transferee to the top
let creator_index = community_mods
.iter()
.position(|r| r.moderator.id == data.person_id)
let creator_person = community_mods.remove(creator_index);
community_mods.insert(0, creator_person);
+ // Delete all the mods
let community_id = data.community_id;
blocking(context.pool(), move |conn| {
CommunityModerator::delete_for_community(conn, community_id)
.await??;
// TODO: this should probably be a bulk operation
+ // Re-add the mods, in the new order
for cmod in &community_mods {
let community_moderator_form = CommunityModeratorForm {
community_id: cmod.community.id,
}
// Mod tables
+ // TODO there should probably be another table for transfer community
+ // Right now, it will just look like it modded them twice
let form = ModAddCommunityForm {
mod_person_id: local_user_view.person.id,
other_person_id: data.person_id,
diesel_option_overwrite_to_url,
source::{
comment::Comment_,
- community::Community_,
local_user::LocalUser_,
password_reset_request::PasswordResetRequest_,
person::Person_,
naive_now,
source::{
comment::Comment,
- community::*,
local_user::{LocalUser, LocalUserForm},
moderator::*,
password_reset_request::*,
.await??;
// Communities
- blocking(context.pool(), move |conn: &'_ _| {
- Community::update_removed_for_creator(conn, banned_person_id, true)
- })
- .await??;
+ // Remove all communities where they're the top mod
+ // TODO couldn't get group by's working in diesel,
+ // for now, remove the communities manually
// Comments
blocking(context.pool(), move |conn: &'_ _| {
description: data.description.to_owned(),
icon,
banner,
- creator_id: local_user_view.person.id,
nsfw: data.nsfw,
actor_id: Some(community_actor_id.to_owned()),
private_key: Some(keypair.private_key),
community::*,
moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
};
-use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_db_views_actor::{
+ community_moderator_view::CommunityModeratorView,
+ community_view::CommunityView,
+};
use lemmy_utils::{utils::naive_from_unix, ApiError, ConnectionId, LemmyError};
use lemmy_websocket::{LemmyContext, UserOperationCrud};
let data: &DeleteCommunity = &self;
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
- // Verify its the creator (only a creator can delete the community)
+ // Fetch the community mods
let community_id = data.community_id;
- let read_community = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
+ let community_mods = blocking(context.pool(), move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
})
.await??;
- if read_community.creator_id != local_user_view.person.id {
+
+ // Make sure deleter is the top mod
+ if local_user_view.person.id != community_mods[0].moderator.id {
return Err(ApiError::err("no_community_edit_allowed").into());
}
let community_form = CommunityForm {
name: read_community.name,
title: data.title.to_owned(),
- creator_id: read_community.creator_id,
description: data.description.to_owned(),
icon,
banner,
name: default_community_name.to_string(),
title: "The Default Community".to_string(),
description: Some("The Default Community".to_string()),
- creator_id: inserted_person.id,
actor_id: Some(actor_id.to_owned()),
private_key: Some(main_community_keypair.private_key),
public_key: Some(main_community_keypair.public_key),
use crate::{
extensions::{context::lemmy_context, group_extension::GroupExtension},
- fetcher::{community::fetch_community_mods, person::get_or_fetch_and_upsert_person},
+ fetcher::community::fetch_community_mods,
generate_moderators_url,
objects::{
check_object_domain,
request_counter: &mut i32,
_mod_action_allowed: bool,
) -> Result<Self, LemmyError> {
- let moderator_uris = fetch_community_mods(context, group, request_counter).await?;
- let creator = if let Some(creator_uri) = moderator_uris.first() {
- get_or_fetch_and_upsert_person(creator_uri, context, request_counter)
- } else {
- // NOTE: code for compatibility with lemmy v0.9.9
- let creator_uri = group
- .inner
- .attributed_to()
- .map(|a| a.as_many())
- .flatten()
- .map(|a| a.first())
- .flatten()
- .map(|a| a.as_xsd_any_uri())
- .flatten()
- .context(location_info!())?;
- get_or_fetch_and_upsert_person(creator_uri, context, request_counter)
- }
- .await?;
+ fetch_community_mods(context, group, request_counter).await?;
let name = group
.inner
name,
title,
description,
- creator_id: creator.id,
removed: None,
published: group.inner.published().map(|u| u.to_owned().naive_local()),
updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
let new_community = CommunityForm {
name: "TIL_comment_agg".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
Person::delete(&conn, another_inserted_person.id).unwrap();
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted);
+
+ // Delete the community
+ let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
+ assert_eq!(1, community_num_deleted);
}
}
let new_community = CommunityForm {
name: "TIL_community_agg".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
let another_community = CommunityForm {
name: "TIL_community_agg_2".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted);
+ // Delete the community
+ let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
+ assert_eq!(1, community_num_deleted);
+
+ let another_community_num_deleted =
+ Community::delete(&conn, another_inserted_community.id).unwrap();
+ assert_eq!(1, another_community_num_deleted);
+
// Should be none found, since the creator was deleted
let after_delete = CommunityAggregates::read(&conn, inserted_community.id);
assert!(after_delete.is_err());
let new_community = CommunityForm {
name: "TIL_site_agg".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
assert_eq!(1, person_num_deleted);
Person::delete(&conn, another_inserted_person.id).unwrap();
+ // Delete the community
+ let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
+ assert_eq!(1, community_num_deleted);
+
// Should be none found
let after_delete = PersonAggregates::read(&conn, inserted_person.id);
assert!(after_delete.is_err());
let new_community = CommunityForm {
name: "TIL_community_agg".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted);
+ // Delete the community
+ let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
+ assert_eq!(1, community_num_deleted);
+
// Should be none found, since the creator was deleted
let after_delete = PostAggregates::read(&conn, inserted_post.id);
assert!(after_delete.is_err());
let new_community = CommunityForm {
name: "TIL_site_agg".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
let person_num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
assert_eq!(1, person_num_deleted);
+ // Delete the community
+ let community_num_deleted = Community::delete(&conn, inserted_community.id).unwrap();
+ assert_eq!(1, community_num_deleted);
+
let after_delete = SiteAggregates::read(&conn);
assert!(after_delete.is_err());
}
let new_community = CommunityForm {
name: "test community".to_string(),
title: "nada".to_owned(),
- creator_id: inserted_person.id,
..CommunityForm::default()
};
name,
title,
description,
- creator_id,
removed,
published,
updated,
name,
title,
description,
- creator_id,
removed,
published,
updated,
community_id: CommunityId,
new_removed: bool,
) -> Result<Community, Error>;
- fn update_removed_for_creator(
- conn: &PgConnection,
- for_creator_id: PersonId,
- new_removed: bool,
- ) -> Result<Vec<Community>, Error>;
- fn update_creator(
- conn: &PgConnection,
- community_id: CommunityId,
- new_creator_id: PersonId,
- ) -> Result<Community, Error>;
fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>;
fn read_from_followers_url(
conn: &PgConnection,
.get_result::<Self>(conn)
}
- fn update_removed_for_creator(
- conn: &PgConnection,
- for_creator_id: PersonId,
- new_removed: bool,
- ) -> Result<Vec<Community>, Error> {
- use lemmy_db_schema::schema::community::dsl::*;
- diesel::update(community.filter(creator_id.eq(for_creator_id)))
- .set((removed.eq(new_removed), updated.eq(naive_now())))
- .get_results::<Self>(conn)
- }
-
- fn update_creator(
- conn: &PgConnection,
- community_id: CommunityId,
- new_creator_id: PersonId,
- ) -> Result<Community, Error> {
- use lemmy_db_schema::schema::community::dsl::*;
- diesel::update(community.find(community_id))
- .set((creator_id.eq(new_creator_id), updated.eq(naive_now())))
- .get_result::<Self>(conn)
- }
-
fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error> {
use lemmy_db_schema::schema::community::dsl::*;
community.select(actor_id).distinct().load::<String>(conn)
let new_community = CommunityForm {
name: "TIL".into(),
- creator_id: inserted_person.id,
title: "nada".to_owned(),
..CommunityForm::default()
};
let expected_community = Community {
id: inserted_community.id,
- creator_id: inserted_person.id,
name: "TIL".into(),
title: "nada".to_owned(),
description: None,
let new_community = CommunityForm {
name: "mod_community".to_string(),
title: "nada".to_owned(),
- creator_id: inserted_person.id,
..CommunityForm::default()
};
let new_community = CommunityForm {
name: "test community lake".to_string(),
title: "nada".to_owned(),
- creator_id: inserted_person.id,
..CommunityForm::default()
};
let new_community = CommunityForm {
name: "test community_3".to_string(),
title: "nada".to_owned(),
- creator_id: inserted_person.id,
..CommunityForm::default()
};
name -> Varchar,
title -> Varchar,
description -> Nullable<Text>,
- creator_id -> Int4,
removed -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
joinable!(comment_report -> comment (comment_id));
joinable!(comment_saved -> comment (comment_id));
joinable!(comment_saved -> person (person_id));
-joinable!(community -> person (creator_id));
joinable!(community_aggregates -> community (community_id));
joinable!(community_follower -> community (community_id));
joinable!(community_follower -> person (person_id));
pub struct CommentForm {
pub creator_id: PersonId,
pub post_id: PostId,
- pub parent_id: Option<CommentId>,
pub content: String,
+ pub parent_id: Option<CommentId>,
pub removed: Option<bool>,
pub read: Option<bool>,
pub published: Option<chrono::NaiveDateTime>,
pub name: String,
pub title: String,
pub description: Option<String>,
- pub creator_id: PersonId,
pub removed: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub name: String,
pub title: String,
pub description: Option<String>,
- pub creator_id: PersonId,
pub removed: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub name: String,
pub title: String,
pub description: Option<String>,
- pub creator_id: PersonId,
pub removed: Option<bool>,
pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
#[table_name = "post"]
pub struct PostForm {
pub name: String,
- pub url: Option<DbUrl>,
- pub body: Option<String>,
pub creator_id: PersonId,
pub community_id: CommunityId,
+ pub nsfw: bool,
+ pub url: Option<DbUrl>,
+ pub body: Option<String>,
pub removed: Option<bool>,
pub locked: Option<bool>,
pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>,
- pub nsfw: bool,
pub stickied: Option<bool>,
pub embed_title: Option<String>,
pub embed_description: Option<String>,
let new_community = CommunityForm {
name: "test community 5".to_string(),
title: "nada".to_owned(),
- creator_id: inserted_person.id,
..CommunityForm::default()
};
local: true,
title: "nada".to_owned(),
description: None,
- creator_id: inserted_person.id,
updated: None,
banner: None,
published: inserted_community.published,
community_person_ban::table.on(
post::community_id
.eq(community_person_ban::community_id)
- .and(community_person_ban::person_id.eq(community::creator_id)),
+ .and(community_person_ban::person_id.eq(post::creator_id)),
),
)
.inner_join(post_aggregates::table)
let new_community = CommunityForm {
name: community_name.to_owned(),
title: "nada".to_owned(),
- creator_id: inserted_person.id,
..CommunityForm::default()
};
local: true,
title: "nada".to_owned(),
description: None,
- creator_id: inserted_person.id,
updated: None,
banner: None,
published: inserted_community.published,
ViewToVec,
};
use lemmy_db_schema::{
- schema::{community, community_aggregates, community_follower, person},
- source::{
- community::{Community, CommunityFollower, CommunitySafe},
- person::{Person, PersonSafe},
- },
+ schema::{community, community_aggregates, community_follower},
+ source::community::{Community, CommunityFollower, CommunitySafe},
CommunityId,
PersonId,
};
#[derive(Debug, Serialize, Clone)]
pub struct CommunityView {
pub community: CommunitySafe,
- pub creator: PersonSafe,
pub subscribed: bool,
pub counts: CommunityAggregates,
}
type CommunityViewTuple = (
CommunitySafe,
- PersonSafe,
CommunityAggregates,
Option<CommunityFollower>,
);
// The left join below will return None in this case
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
- let (community, creator, counts, follower) = community::table
+ let (community, counts, follower) = community::table
.find(community_id)
- .inner_join(person::table)
.inner_join(community_aggregates::table)
.left_join(
community_follower::table.on(
)
.select((
Community::safe_columns_tuple(),
- Person::safe_columns_tuple(),
community_aggregates::all_columns,
community_follower::all_columns.nullable(),
))
Ok(CommunityView {
community,
- creator,
subscribed: follower.is_some(),
counts,
})
let person_id_join = self.my_person_id.unwrap_or(PersonId(-1));
let mut query = community::table
- .inner_join(person::table)
.inner_join(community_aggregates::table)
.left_join(
community_follower::table.on(
)
.select((
Community::safe_columns_tuple(),
- Person::safe_columns_tuple(),
community_aggregates::all_columns,
community_follower::all_columns.nullable(),
))
.iter()
.map(|a| Self {
community: a.0.to_owned(),
- creator: a.1.to_owned(),
- counts: a.2.to_owned(),
- subscribed: a.3.is_some(),
+ counts: a.1.to_owned(),
+ subscribed: a.2.is_some(),
})
.collect::<Vec<Self>>()
}
--- /dev/null
+
+-- Add the column back
+alter table community add column creator_id int references person on update cascade on delete cascade;
+
+-- Recreate the index
+create index idx_community_creator on community (creator_id);
+
+-- Add the data, selecting the highest mod
+update community
+set creator_id = sub.person_id
+from (
+ select
+ cm.community_id,
+ cm.person_id
+ from
+ community_moderator cm
+ limit 1
+) as sub
+where id = sub.community_id;
+
+-- Set to not null
+alter table community alter column creator_id set not null;
+
+
--- /dev/null
+-- Drop the column
+alter table community drop column creator_id;
name: ccommunity.name.to_owned(),
title: ccommunity.title.to_owned(),
description: ccommunity.description.to_owned(),
- creator_id: ccommunity.creator_id,
removed: None,
deleted: None,
nsfw: None,