From 6c7ddedd342553b53dd98c8de9cbe9e8e2e8cd7c Mon Sep 17 00:00:00 2001 From: Michael Manfre Date: Sun, 27 Nov 2022 13:09:46 -0500 Subject: Post editing --- activities/models/fan_out.py | 86 ++++++++++++++++++++++++++++++-------------- activities/models/post.py | 40 +++++++++++++++++++++ 2 files changed, 99 insertions(+), 27 deletions(-) (limited to 'activities/models') diff --git a/activities/models/fan_out.py b/activities/models/fan_out.py index 64df929..a86e30a 100644 --- a/activities/models/fan_out.py +++ b/activities/models/fan_out.py @@ -17,11 +17,15 @@ class FanOutStates(StateGraph): """ Sends the fan-out to the right inbox. """ + LOCAL_IDENTITY = True + REMOTE_IDENTITY = False + fan_out = await instance.afetch_full() - # Handle Posts - if fan_out.type == FanOut.Types.post: - post = await fan_out.subject_post.afetch_full() - if fan_out.identity.local: + + match (fan_out.type, fan_out.identity.local): + # Handle creating/updating local posts + case (FanOut.Types.post | FanOut.Types.post_edited, LOCAL_IDENTITY): + post = await fan_out.subject_post.afetch_full() # Make a timeline event directly # If it's a reply, we only add it if we follow at least one # of the people mentioned. @@ -44,63 +48,91 @@ class FanOutStates(StateGraph): identity=fan_out.identity, post=post, ) - else: + + # Handle sending remote posts create + case (FanOut.Types.post, REMOTE_IDENTITY): + post = await fan_out.subject_post.afetch_full() # Sign it and send it await post.author.signed_request( method="post", uri=fan_out.identity.inbox_uri, body=canonicalise(post.to_create_ap()), ) - # Handle deleting posts - elif fan_out.type == FanOut.Types.post_deleted: - post = await fan_out.subject_post.afetch_full() - if fan_out.identity.local: - # Remove all timeline events mentioning it - await TimelineEvent.objects.filter( - identity=fan_out.identity, - subject_post=post, - ).adelete() - else: + + # Handle sending remote posts update + case (FanOut.Types.post_edited, REMOTE_IDENTITY): + post = await fan_out.subject_post.afetch_full() + # Sign it and send it + await post.author.signed_request( + method="post", + uri=fan_out.identity.inbox_uri, + body=canonicalise(post.to_update_ap()), + ) + + # Handle deleting local posts + case (FanOut.Types.post_deleted, LOCAL_IDENTITY): + post = await fan_out.subject_post.afetch_full() + if fan_out.identity.local: + # Remove all timeline events mentioning it + await TimelineEvent.objects.filter( + identity=fan_out.identity, + subject_post=post, + ).adelete() + + # Handle sending remote post deletes + case (FanOut.Types.post_deleted, REMOTE_IDENTITY): + post = await fan_out.subject_post.afetch_full() # Send it to the remote inbox await post.author.signed_request( method="post", uri=fan_out.identity.inbox_uri, body=canonicalise(post.to_delete_ap()), ) - # Handle boosts/likes - elif fan_out.type == FanOut.Types.interaction: - interaction = await fan_out.subject_post_interaction.afetch_full() - if fan_out.identity.local: + + # Handle local boosts/likes + case (FanOut.Types.interaction, LOCAL_IDENTITY): + interaction = await fan_out.subject_post_interaction.afetch_full() # Make a timeline event directly await sync_to_async(TimelineEvent.add_post_interaction)( identity=fan_out.identity, interaction=interaction, ) - else: + + # Handle sending remote boosts/likes + case (FanOut.Types.interaction, REMOTE_IDENTITY): + interaction = await fan_out.subject_post_interaction.afetch_full() # Send it to the remote inbox await interaction.identity.signed_request( method="post", uri=fan_out.identity.inbox_uri, body=canonicalise(interaction.to_ap()), ) - # Handle undoing boosts/likes - elif fan_out.type == FanOut.Types.undo_interaction: - interaction = await fan_out.subject_post_interaction.afetch_full() - if fan_out.identity.local: + + # Handle undoing local boosts/likes + case (FanOut.Types.undo_interaction, LOCAL_IDENTITY): # noqa:F841 + interaction = await fan_out.subject_post_interaction.afetch_full() + # Delete any local timeline events await sync_to_async(TimelineEvent.delete_post_interaction)( identity=fan_out.identity, interaction=interaction, ) - else: + + # Handle sending remote undoing boosts/likes + case (FanOut.Types.undo_interaction, REMOTE_IDENTITY): # noqa:F841 + interaction = await fan_out.subject_post_interaction.afetch_full() # Send an undo to the remote inbox await interaction.identity.signed_request( method="post", uri=fan_out.identity.inbox_uri, body=canonicalise(interaction.to_undo_ap()), ) - else: - raise ValueError(f"Cannot fan out with type {fan_out.type}") + + case _: + raise ValueError( + f"Cannot fan out with type {fan_out.type} local={fan_out.identity.local}" + ) + return cls.sent diff --git a/activities/models/post.py b/activities/models/post.py index f75c526..23194b3 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -22,9 +22,17 @@ class PostStates(StateGraph): deleted = State(try_interval=300) deleted_fanned_out = State() + edited = State(try_interval=300) + edited_fanned_out = State(externally_progressed=True) + new.transitions_to(fanned_out) fanned_out.transitions_to(deleted) + fanned_out.transitions_to(edited) + deleted.transitions_to(deleted_fanned_out) + edited.transitions_to(edited_fanned_out) + edited_fanned_out.transitions_to(edited) + edited_fanned_out.transitions_to(deleted) @classmethod async def handle_new(cls, instance: "Post"): @@ -56,6 +64,21 @@ class PostStates(StateGraph): ) return cls.deleted_fanned_out + @classmethod + async def handle_edited(cls, instance: "Post"): + """ + Creates all needed fan-out objects for an edited Post. + """ + post = await instance.afetch_full() + # Fan out to each target + for follow in await post.aget_targets(): + await FanOut.objects.acreate( + identity=follow, + type=FanOut.Types.post_edited, + subject_post=post, + ) + return cls.edited_fanned_out + class Post(StatorModel): """ @@ -140,6 +163,7 @@ class Post(StatorModel): action_boost = "{view}boost/" action_unboost = "{view}unboost/" action_delete = "{view}delete/" + action_edit = "{view}edit/" action_reply = "/compose/?reply_to={self.id}" def get_scheme(self, url): @@ -305,6 +329,8 @@ class Post(StatorModel): value["summary"] = self.summary if self.in_reply_to: value["inReplyTo"] = self.in_reply_to + if self.edited: + value["updated"] = format_ld_date(self.edited) # Mentions for mention in self.mentions.all(): value["tag"].append( @@ -336,6 +362,20 @@ class Post(StatorModel): "object": object, } + def to_update_ap(self): + """ + Returns the AP JSON to update this object + """ + object = self.to_ap() + return { + "to": object["to"], + "cc": object.get("cc", []), + "type": "Update", + "id": self.object_uri + "#update", + "actor": self.author.actor_uri, + "object": object, + } + def to_delete_ap(self): """ Returns the AP JSON to create this object -- cgit v1.2.3