"reqwest",
"reqwest-middleware",
"serde",
+ "serde_json",
"sha2",
"tracing",
"url",
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ community::{CommunityResponse, HideCommunity},
+ get_local_user_view_from_jwt,
+ is_admin,
+};
+use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
+use lemmy_db_schema::{
+ naive_now,
+ source::{
+ community::{Community, CommunityForm},
+ moderator::{ModHideCommunity, ModHideCommunityForm},
+ },
+ traits::Crud,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for HideCommunity {
+ type Response = CommunityResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<CommunityResponse, LemmyError> {
+ let data: &HideCommunity = self;
+
+ // Verify its a admin (only admin can hide or unhide it)
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+ is_admin(&local_user_view)?;
+
+ let community_id = data.community_id;
+ let read_community = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+
+ let community_form = CommunityForm {
+ name: read_community.name,
+ title: read_community.title,
+ description: read_community.description.to_owned(),
+ hidden: Some(data.hidden),
+ updated: Some(naive_now()),
+ ..CommunityForm::default()
+ };
+
+ let mod_hide_community_form = ModHideCommunityForm {
+ community_id: data.community_id,
+ mod_person_id: local_user_view.person.id,
+ reason: data.reason.clone(),
+ hidden: Some(data.hidden),
+ };
+
+ let community_id = data.community_id;
+ let updated_community = blocking(context.pool(), move |conn| {
+ Community::update(conn, community_id, &community_form)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community_hidden_status"))?;
+
+ blocking(context.pool(), move |conn| {
+ ModHideCommunity::create(conn, &mod_hide_community_form)
+ })
+ .await??;
+
+ UpdateCommunity::send(
+ updated_community.into(),
+ &local_user_view.person.into(),
+ context,
+ )
+ .await?;
+
+ let op = UserOperationCrud::EditCommunity;
+ send_community_ws_message(data.community_id, op, websocket_id, None, context).await
+ }
+}
mod ban;
mod block;
mod follow;
+mod hide;
mod transfer;
use lemmy_api_common::{
blocking,
check_image_has_local_domain,
- community::{CommunityResponse, EditCommunity, HideCommunity},
+ community::{CommunityResponse, EditCommunity},
get_local_user_view_from_jwt,
- is_admin,
};
use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
use lemmy_db_schema::{
diesel_option_overwrite_to_url,
naive_now,
newtypes::PersonId,
- source::{
- community::{Community, CommunityForm},
- moderator::{ModHideCommunity, ModHideCommunityForm},
- },
+ source::community::{Community, CommunityForm},
traits::Crud,
};
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
name: read_community.name,
title: data.title.to_owned().unwrap_or(read_community.title),
description: data.description.to_owned(),
- public_key: read_community.public_key,
icon,
banner,
nsfw: data.nsfw,
- hidden: Some(read_community.hidden),
updated: Some(naive_now()),
..CommunityForm::default()
};
send_community_ws_message(data.community_id, op, websocket_id, None, context).await
}
}
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for HideCommunity {
- type Response = CommunityResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<CommunityResponse, LemmyError> {
- let data: &HideCommunity = self;
-
- // Verify its a admin (only admin can hide or unhide it)
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
- is_admin(&local_user_view)?;
-
- let community_id = data.community_id;
- let read_community = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
- })
- .await??;
-
- let community_form = CommunityForm {
- name: read_community.name,
- title: read_community.title,
- description: read_community.description.to_owned(),
- public_key: read_community.public_key,
- icon: Some(read_community.icon),
- banner: Some(read_community.banner),
- nsfw: Some(read_community.nsfw),
- updated: Some(naive_now()),
- hidden: Some(data.hidden),
- ..CommunityForm::default()
- };
-
- let mod_hide_community_form = ModHideCommunityForm {
- community_id: data.community_id,
- mod_person_id: local_user_view.person.id,
- reason: data.reason.clone(),
- hidden: Some(data.hidden),
- };
-
- let community_id = data.community_id;
- let updated_community = blocking(context.pool(), move |conn| {
- Community::update(conn, community_id, &community_form)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community_hidden_status"))?;
-
- blocking(context.pool(), move |conn| {
- ModHideCommunity::create(conn, &mod_hide_community_form)
- })
- .await??;
-
- UpdateCommunity::send(
- updated_community.into(),
- &local_user_view.person.into(),
- context,
- )
- .await?;
-
- let op = UserOperationCrud::EditCommunity;
- send_community_ws_message(data.community_id, op, websocket_id, None, context).await
- }
-}
EndpointType,
};
use lemmy_db_schema::{
- source::post::{Post, PostForm, PostLike, PostLikeForm},
+ source::{
+ community::Community,
+ post::{Post, PostForm, PostLike, PostLikeForm},
+ },
traits::{Crud, Likeable},
};
+use lemmy_db_views_actor::community_view::CommunityView;
use lemmy_utils::{
request::fetch_site_data,
utils::{
check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
check_community_deleted_or_removed(data.community_id, context.pool()).await?;
+ let community_id = data.community_id;
+ let community = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+ if community.posting_restricted_to_mods {
+ let community_id = data.community_id;
+ let is_mod = blocking(context.pool(), move |conn| {
+ CommunityView::is_mod_or_admin(conn, local_user_view.local_user.person_id, community_id)
+ })
+ .await?;
+ if !is_mod {
+ return Err(LemmyError::from_message("only_mods_can_post_in_community"));
+ }
+ }
+
// Fetch post links and pictrs cached image
let data_url = data.url.as_ref();
let (metadata_res, pictrs_thumbnail) =
},
"sensitive": false,
"moderators": "http://enterprise.lemmy.ml/c/main/moderators",
+ "postingRestrictedToMods": false,
"inbox": "http://enterprise.lemmy.ml/c/main/inbox",
"outbox": "http://enterprise.lemmy.ml/c/main/outbox",
"followers": "http://enterprise.lemmy.ml/c/main/followers",
"inbox": "https://enterprise.lemmy.ml/c/tenforward/inbox",
"followers": "https://enterprise.lemmy.ml/c/tenforward/followers",
"moderators": "https://enterprise.lemmy.ml/c/tenforward/moderators",
+ "postingRestrictedToMods": false,
"endpoints": {
"sharedInbox": "https://enterprise.lemmy.ml/inbox"
},
public_key: self.get_public_key()?,
published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
+ posting_restricted_to_mods: Some(self.posting_restricted_to_mods),
};
Ok(group)
}
pub(crate) sensitive: Option<bool>,
// lemmy extension
pub(crate) moderators: Option<ObjectId<ApubCommunityModerators>>,
+ // lemmy extension
+ pub(crate) posting_restricted_to_mods: Option<bool>,
pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
pub(crate) endpoints: Option<Endpoints>,
pub(crate) published: Option<DateTime<FixedOffset>>,
followers_url: Some(self.followers.into()),
inbox_url: Some(self.inbox.into()),
shared_inbox_url: Some(self.endpoints.map(|e| e.shared_inbox.into())),
+ posting_restricted_to_mods: self.posting_restricted_to_mods,
}
}
}
serde = { version = "1.0.136", features = ["derive"] }
async-trait = "0.1.53"
url = { version = "2.2.2", features = ["serde"] }
+serde_json = { version = "1.0.79", features = ["preserve_order"] }
anyhow = "1.0.56"
reqwest = { version = "0.11.10", features = ["json"] }
reqwest-middleware = "0.1.5"
icon,
banner,
hidden,
+ posting_restricted_to_mods,
);
impl ToSafe for Community {
icon,
banner,
hidden,
+ posting_restricted_to_mods,
)
}
}
inbox_url: inserted_community.inbox_url.to_owned(),
shared_inbox_url: None,
hidden: false,
+ posting_restricted_to_mods: false,
};
let community_follower_form = CommunityFollowerForm {
inbox_url -> Varchar,
shared_inbox_url -> Nullable<Varchar>,
hidden -> Bool,
+ posting_restricted_to_mods -> Bool,
}
}
pub inbox_url: DbUrl,
pub shared_inbox_url: Option<DbUrl>,
pub hidden: bool,
+ pub posting_restricted_to_mods: bool,
}
/// A safe representation of community, without the sensitive info
pub icon: Option<DbUrl>,
pub banner: Option<DbUrl>,
pub hidden: bool,
+ pub posting_restricted_to_mods: bool,
}
#[derive(Insertable, AsChangeset, Debug, Default)]
pub inbox_url: Option<DbUrl>,
pub shared_inbox_url: Option<Option<DbUrl>>,
pub hidden: Option<bool>,
+ pub posting_restricted_to_mods: Option<bool>,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
updated: None,
banner: None,
hidden: false,
+ posting_restricted_to_mods: false,
published: inserted_community.published,
},
creator: PersonSafe {
updated: None,
banner: None,
hidden: false,
+ posting_restricted_to_mods: false,
published: inserted_community.published,
},
counts: CommentAggregates {
updated: None,
banner: None,
hidden: false,
+ posting_restricted_to_mods: false,
published: inserted_community.published,
},
creator: PersonSafe {
updated: None,
banner: None,
hidden: false,
+ posting_restricted_to_mods: false,
published: inserted_community.published,
},
counts: PostAggregates {
})
}
- // TODO: this function is only used by is_mod_or_admin() below, can probably be merged
- fn community_mods_and_admins(
- conn: &PgConnection,
- community_id: CommunityId,
- ) -> Result<Vec<PersonId>, Error> {
- let mut mods_and_admins: Vec<PersonId> = Vec::new();
- mods_and_admins.append(
- &mut CommunityModeratorView::for_community(conn, community_id)
- .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?,
- );
- mods_and_admins.append(
- &mut PersonViewSafe::admins(conn).map(|v| v.into_iter().map(|a| a.person.id).collect())?,
- );
- Ok(mods_and_admins)
- }
-
pub fn is_mod_or_admin(
conn: &PgConnection,
person_id: PersonId,
community_id: CommunityId,
) -> bool {
- Self::community_mods_and_admins(conn, community_id)
+ let is_mod = CommunityModeratorView::for_community(conn, community_id)
+ .map(|v| {
+ v.into_iter()
+ .map(|m| m.moderator.id)
+ .collect::<Vec<PersonId>>()
+ })
+ .unwrap_or_default()
+ .contains(&person_id);
+ if is_mod {
+ return true;
+ }
+
+ PersonViewSafe::admins(conn)
+ .map(|v| {
+ v.into_iter()
+ .map(|a| a.person.id)
+ .collect::<Vec<PersonId>>()
+ })
.unwrap_or_default()
.contains(&person_id)
}
--- /dev/null
+alter table community drop column posting_restricted_to_mods;
\ No newline at end of file
--- /dev/null
+alter table community add column posting_restricted_to_mods boolean default false;
\ No newline at end of file
.wrap(rate_limit.message())
.route("", web::get().to(route_get_crud::<GetCommunity>))
.route("", web::put().to(route_post_crud::<EditCommunity>))
- .route("/hide", web::put().to(route_post_crud::<HideCommunity>))
+ .route("/hide", web::put().to(route_post::<HideCommunity>))
.route("/list", web::get().to(route_get_crud::<ListCommunities>))
.route("/follow", web::post().to(route_post::<FollowCommunity>))
.route("/block", web::post().to(route_post::<BlockCommunity>))
name: ccommunity.name.to_owned(),
title: ccommunity.title.to_owned(),
description: ccommunity.description.to_owned(),
- removed: None,
- deleted: None,
- nsfw: None,
- updated: None,
hidden: Some(false),
actor_id: Some(community_actor_id.to_owned()),
local: Some(ccommunity.local),
private_key: Some(Some(keypair.private_key)),
public_key: keypair.public_key,
last_refreshed_at: Some(naive_now()),
- published: None,
icon: Some(ccommunity.icon.to_owned()),
banner: Some(ccommunity.banner.to_owned()),
- followers_url: None,
- inbox_url: None,
- shared_inbox_url: None,
+ ..Default::default()
};
Community::update(conn, ccommunity.id, &form)?;