]> Untitled Git - lemmy.git/commitdiff
Implement deleting communities
authorFelix <me@nutomic.com>
Tue, 28 Apr 2020 17:46:25 +0000 (19:46 +0200)
committerFelix <me@nutomic.com>
Tue, 28 Apr 2020 17:46:25 +0000 (19:46 +0200)
server/src/api/community.rs
server/src/apub/comment.rs
server/src/apub/community.rs
server/src/apub/mod.rs
server/src/apub/post.rs
server/src/apub/shared_inbox.rs
server/src/apub/user.rs

index 296a77eaf43e94e170df51204b27a6fcc6284e45..7610d1b7847b1dd86cb36b42f272d70702035e82 100644 (file)
@@ -358,7 +358,7 @@ impl Perform for Oper<EditCommunity> {
       published: None,
     };
 
-    let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
+    let updated_community = match Community::update(&conn, data.edit_id, &community_form) {
       Ok(community) => community,
       Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
     };
@@ -377,6 +377,11 @@ impl Perform for Oper<EditCommunity> {
         expires,
       };
       ModRemoveCommunity::create(&conn, &form)?;
+      updated_community.send_delete(&conn)?;
+    }
+
+    if let Some(_deleted) = data.deleted.to_owned() {
+      updated_community.send_delete(&conn)?;
     }
 
     let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
index 6cede17b277e9bb05694d017f64ef06d5c0cd904..9fa5731ba1cf16f9a1e54c329831c7e5d5b74122 100644 (file)
@@ -3,7 +3,7 @@ use super::*;
 impl ToApub for Comment {
   type Response = Note;
 
-  fn to_apub(&self, conn: &PgConnection) -> Result<Note, Error> {
+  fn to_apub(&self, conn: &PgConnection) -> Result<ResponseOrTombstone<Note>, Error> {
     let mut comment = Note::default();
     let oprops: &mut ObjectProperties = comment.as_mut();
     let creator = User_::read(&conn, self.creator_id)?;
@@ -33,7 +33,7 @@ impl ToApub for Comment {
       oprops.set_updated(convert_datetime(u))?;
     }
 
-    Ok(comment)
+    Ok(ResponseOrTombstone::Response(comment))
   }
 }
 
@@ -102,7 +102,7 @@ impl ApubObjectType for Comment {
     create
       .create_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(note)?;
+      .set_object_base_box(note.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
@@ -138,7 +138,7 @@ impl ApubObjectType for Comment {
     update
       .update_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(note)?;
+      .set_object_base_box(note.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
@@ -171,7 +171,7 @@ impl ApubLikeableType for Comment {
     like
       .like_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(note)?;
+      .set_object_base_box(note.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
@@ -206,7 +206,7 @@ impl ApubLikeableType for Comment {
     dislike
       .dislike_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(note)?;
+      .set_object_base_box(note.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
index 46bd9024c03f3cb2c2985f09f38f8dc81e0a7d46..ee3199954e2ae86850034b22e8e27fab011595d4 100644 (file)
@@ -9,7 +9,17 @@ impl ToApub for Community {
   type Response = GroupExt;
 
   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
-  fn to_apub(&self, conn: &PgConnection) -> Result<GroupExt, Error> {
+  fn to_apub(&self, conn: &PgConnection) -> Result<ResponseOrTombstone<GroupExt>, Error> {
+    if self.deleted || self.removed {
+      let mut tombstone = Tombstone::default();
+      // TODO: might want to include updated/deleted times as well
+      tombstone
+        .object_props
+        .set_id(self.actor_id.to_owned())?
+        .set_published(convert_datetime(self.published))?;
+      return Ok(ResponseOrTombstone::Tombstone(Box::new(tombstone)));
+    }
+
     let mut group = Group::default();
     let oprops: &mut ObjectProperties = group.as_mut();
 
@@ -43,7 +53,9 @@ impl ToApub for Community {
       .set_endpoints(endpoint_props)?
       .set_followers(self.get_followers_url())?;
 
-    Ok(group.extend(actor_props).extend(self.get_public_key_ext()))
+    Ok(ResponseOrTombstone::Response(
+      group.extend(actor_props).extend(self.get_public_key_ext()),
+    ))
   }
 }
 
@@ -94,10 +106,36 @@ impl ActorType for Community {
     Ok(())
   }
 
+  fn send_delete(&self, conn: &PgConnection) -> Result<(), Error> {
+    let community = self.to_apub(conn)?;
+    let mut delete = Delete::default();
+    delete
+      .delete_props
+      .set_actor_xsd_any_uri(self.actor_id.to_owned())?
+      .set_object_base_box(BaseBox::from_concrete(
+        community.as_tombstone()?.to_owned(),
+      )?)?;
+
+    // Insert the sent activity into the activity table
+    let activity_form = activity::ActivityForm {
+      user_id: self.creator_id,
+      data: serde_json::to_value(&delete)?,
+      local: true,
+      updated: None,
+    };
+    activity::Activity::create(&conn, &activity_form)?;
+
+    send_activity(
+      &delete,
+      &self.private_key.to_owned().unwrap(),
+      &self.actor_id,
+      self.get_follower_inboxes(&conn)?,
+    )?;
+    Ok(())
+  }
+
   /// For a given community, returns the inboxes of all followers.
   fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
-    debug!("got here.");
-
     Ok(
       CommunityFollowerView::for_community(conn, self.id)?
         .into_iter()
index 4b08c53aa8a7f3afd57526c7057964ec63518ac3..9232c2d7e3c4f3880012a8235abf8ab72b3e49e4 100644 (file)
@@ -10,13 +10,13 @@ pub mod user;
 pub mod user_inbox;
 
 use activitystreams::{
-  activity::{Accept, Create, Dislike, Follow, Like, Update},
+  activity::{Accept, Create, Delete, Dislike, Follow, Like, Update},
   actor::{properties::ApActorProperties, Actor, Group, Person},
   collection::UnorderedCollection,
   context,
   endpoint::EndpointProperties,
   ext::{Ext, Extensible, Extension},
-  object::{properties::ObjectProperties, Note, Page},
+  object::{properties::ObjectProperties, Note, Page, Tombstone},
   public, BaseBox,
 };
 use actix_web::body::Body;
@@ -138,10 +138,31 @@ fn is_apub_id_valid(apub_id: &Url) -> bool {
   }
 }
 
+#[derive(Serialize)]
+pub enum ResponseOrTombstone<Response> {
+  Response(Response),
+  Tombstone(Box<Tombstone>),
+}
+
+impl<Response> ResponseOrTombstone<Response> {
+  fn as_response(&self) -> Result<&Response, Error> {
+    match self {
+      ResponseOrTombstone::Response(r) => Ok(r),
+      ResponseOrTombstone::Tombstone(_t) => Err(format_err!("Value is a tombstone")),
+    }
+  }
+  fn as_tombstone(&self) -> Result<&Tombstone, Error> {
+    match self {
+      ResponseOrTombstone::Tombstone(t) => Ok(t),
+      ResponseOrTombstone::Response(_r) => Err(format_err!("Value is a response")),
+    }
+  }
+}
+
 // TODO Not sure good names for these
 pub trait ToApub {
   type Response;
-  fn to_apub(&self, conn: &PgConnection) -> Result<Self::Response, Error>;
+  fn to_apub(&self, conn: &PgConnection) -> Result<ResponseOrTombstone<Self::Response>, Error>;
 }
 
 pub trait FromApub {
@@ -154,6 +175,7 @@ pub trait FromApub {
 pub trait ApubObjectType {
   fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
   fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
+  //fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
 }
 
 pub trait ApubLikeableType {
@@ -192,6 +214,8 @@ pub trait ActorType {
     Err(format_err!("Accept not implemented."))
   }
 
+  fn send_delete(&self, conn: &PgConnection) -> Result<(), Error>;
+
   // TODO default because there is no user following yet.
   #[allow(unused_variables)]
   /// For a given community, returns the inboxes of all followers.
index af8ee5998b154fc8c8a18097a5da81d9a9a31536..381ba3c6cf939a2546d4f267363bd7ee657be942 100644 (file)
@@ -19,7 +19,7 @@ impl ToApub for Post {
   type Response = Page;
 
   // Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
-  fn to_apub(&self, conn: &PgConnection) -> Result<Page, Error> {
+  fn to_apub(&self, conn: &PgConnection) -> Result<ResponseOrTombstone<Page>, Error> {
     let mut page = Page::default();
     let oprops: &mut ObjectProperties = page.as_mut();
     let creator = User_::read(conn, self.creator_id)?;
@@ -51,7 +51,7 @@ impl ToApub for Post {
       oprops.set_updated(convert_datetime(u))?;
     }
 
-    Ok(page)
+    Ok(ResponseOrTombstone::Response(page))
   }
 }
 
@@ -109,7 +109,7 @@ impl ApubObjectType for Post {
     create
       .create_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(page)?;
+      .set_object_base_box(page.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
@@ -144,7 +144,7 @@ impl ApubObjectType for Post {
     update
       .update_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(page)?;
+      .set_object_base_box(page.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
@@ -176,7 +176,7 @@ impl ApubLikeableType for Post {
     like
       .like_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(page)?;
+      .set_object_base_box(page.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
@@ -210,7 +210,7 @@ impl ApubLikeableType for Post {
     dislike
       .dislike_props
       .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
-      .set_object_base_box(page)?;
+      .set_object_base_box(page.as_response()?.to_owned())?;
 
     // Insert the sent activity into the activity table
     let activity_form = activity::ActivityForm {
index 705a578a0194e58cd3b6fe4e86ac95be9e8ca156..10ed1122dda6bff5cb0dab9af3c8087af11dff73 100644 (file)
@@ -1,4 +1,6 @@
 use super::*;
+use crate::api::community::CommunityResponse;
+use crate::websocket::server::SendCommunityRoomMessage;
 
 #[serde(untagged)]
 #[derive(Serialize, Deserialize, Debug)]
@@ -7,6 +9,7 @@ pub enum SharedAcceptedObjects {
   Update(Update),
   Like(Like),
   Dislike(Dislike),
+  Delete(Delete),
 }
 
 impl SharedAcceptedObjects {
@@ -16,6 +19,7 @@ impl SharedAcceptedObjects {
       SharedAcceptedObjects::Update(u) => u.update_props.get_object_base_box(),
       SharedAcceptedObjects::Like(l) => l.like_props.get_object_base_box(),
       SharedAcceptedObjects::Dislike(d) => d.dislike_props.get_object_base_box(),
+      SharedAcceptedObjects::Delete(d) => d.delete_props.get_object_base_box(),
     }
   }
 }
@@ -61,6 +65,9 @@ pub async fn shared_inbox(
     (SharedAcceptedObjects::Dislike(d), Some("Note")) => {
       receive_dislike_comment(&d, &request, &conn, chat_server)
     }
+    (SharedAcceptedObjects::Delete(d), Some("Tombstone")) => {
+      receive_delete_community(&d, &request, &conn, chat_server)
+    }
     _ => Err(format_err!("Unknown incoming activity type.")),
   }
 }
@@ -502,3 +509,64 @@ fn receive_dislike_comment(
 
   Ok(HttpResponse::Ok().finish())
 }
+
+fn receive_delete_community(
+  delete: &Delete,
+  request: &HttpRequest,
+  conn: &PgConnection,
+  chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+  let tombstone = delete
+    .delete_props
+    .get_object_base_box()
+    .to_owned()
+    .unwrap()
+    .to_owned()
+    .to_concrete::<Tombstone>()?;
+  let community_apub_id = tombstone.object_props.get_id().unwrap().to_string();
+
+  let community = Community::read_from_actor_id(conn, &community_apub_id)?;
+  verify(request, &community.public_key.clone().unwrap())?;
+
+  // Insert the received activity into the activity table
+  let activity_form = activity::ActivityForm {
+    user_id: community.creator_id,
+    data: serde_json::to_value(&delete)?,
+    local: false,
+    updated: None,
+  };
+  activity::Activity::create(&conn, &activity_form)?;
+
+  let community_form = CommunityForm {
+    name: "".to_string(),
+    title: "".to_string(),
+    description: None,
+    category_id: community.category_id, // Note: need to keep this due to foreign key constraint
+    creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
+    removed: None,
+    published: None,
+    updated: None,
+    deleted: Some(true),
+    nsfw: false,
+    actor_id: community.actor_id,
+    local: false,
+    private_key: None,
+    public_key: community.public_key,
+    last_refreshed_at: Some(community.last_refreshed_at),
+  };
+
+  Community::update(conn, community.id, &community_form)?;
+
+  let res = CommunityResponse {
+    community: CommunityView::read(&conn, community.id, None)?,
+  };
+
+  chat_server.do_send(SendCommunityRoomMessage {
+    op: UserOperation::EditCommunity,
+    response: res,
+    community_id: community.id,
+    my_id: None,
+  });
+
+  Ok(HttpResponse::Ok().finish())
+}
index d7fd228268bc338dc7aae9af1dbfef114ca3dca8..36147f7a0d388d2ac8197186d045ced8faafc2ab 100644 (file)
@@ -9,7 +9,7 @@ impl ToApub for User_ {
   type Response = PersonExt;
 
   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
-  fn to_apub(&self, _conn: &PgConnection) -> Result<PersonExt, Error> {
+  fn to_apub(&self, _conn: &PgConnection) -> Result<ResponseOrTombstone<PersonExt>, Error> {
     // TODO go through all these to_string and to_owned()
     let mut person = Person::default();
     let oprops: &mut ObjectProperties = person.as_mut();
@@ -41,7 +41,9 @@ impl ToApub for User_ {
       .set_following(self.get_following_url())?
       .set_liked(self.get_liked_url())?;
 
-    Ok(person.extend(actor_props).extend(self.get_public_key_ext()))
+    Ok(ResponseOrTombstone::Response(
+      person.extend(actor_props).extend(self.get_public_key_ext()),
+    ))
   }
 }
 
@@ -87,6 +89,10 @@ impl ActorType for User_ {
     )?;
     Ok(())
   }
+
+  fn send_delete(&self, _conn: &PgConnection) -> Result<(), Error> {
+    unimplemented!()
+  }
 }
 
 impl FromApub for UserForm {