initial work to add 'Move' activity

This commit is contained in:
Hugh Rundle 2023-08-29 21:07:41 +10:00
parent e5f8e4babc
commit e7ba6a3141
No known key found for this signature in database
GPG key ID: A7E35779918253F9
6 changed files with 121 additions and 1 deletions

View file

@ -19,6 +19,7 @@ from .verbs import Create, Delete, Undo, Update
from .verbs import Follow, Accept, Reject, Block from .verbs import Follow, Accept, Reject, Block
from .verbs import Add, Remove from .verbs import Add, Remove
from .verbs import Announce, Like from .verbs import Announce, Like
from .verbs import Move
# this creates a list of all the Activity types that we can serialize, # this creates a list of all the Activity types that we can serialize,
# so when an Activity comes in from outside, we can check if it's known # so when an Activity comes in from outside, we can check if it's known

View file

@ -231,3 +231,28 @@ class Announce(Verb):
def action(self, allow_external_connections=True): def action(self, allow_external_connections=True):
"""boost""" """boost"""
self.to_model(allow_external_connections=allow_external_connections) self.to_model(allow_external_connections=allow_external_connections)
@dataclass(init=False)
class Move(Verb):
"""a user moving an object"""
# note the spec example for target and origin is an object but
# Mastodon uses a URI string and TBH this makes more sense
# Is there a way we can account for either format?
object: str
type: str = "Move"
target: str
origin: str
def action(self, allow_external_connections=True):
"""move"""
# we need to work out whether the object is a user or something else.
object_is_user = True # TODO!
if object_is_user:
self.to_model(object_is_user=True allow_external_connections=allow_external_connections)
else:
self.to_model(object_is_user=False allow_external_connections=allow_external_connections)

50
bookwyrm/models/move.py Normal file
View file

@ -0,0 +1,50 @@
""" move an object including migrating a user account """
from django.db import models
from bookwyrm import activitypub
from .activitypub_mixin import ActivityMixin
from .base_model import BookWyrmModel
from . import fields
from .status import Status
class Move(ActivityMixin, BookWyrmModel):
"""migrating an activitypub user account"""
user = fields.ForeignKey(
"User", on_delete=models.PROTECT, activitypub_field="actor"
)
# TODO: can we just use the abstract class here?
activitypub_object = fields.ForeignKey(
"BookWyrmModel", on_delete=models.PROTECT,
activitypub_field="object",
blank=True,
null=True
)
target = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
origin = fields.CharField(
max_length=255, blank=True, null=True, deduplication_field=True
)
activity_serializer = activitypub.Move
# pylint: disable=unused-argument
@classmethod
def ignore_activity(cls, activity, allow_external_connections=True):
"""don't bother with incoming moves of unknown objects"""
# TODO how do we check this for any conceivable object?
pass
def save(self, *args, **kwargs):
"""update user active time"""
self.user.update_active_date()
super().save(*args, **kwargs)
# Ok what else? We can trigger a notification for followers of a user who sends a `Move` for themselves
# What about when a book is merged (i.e. moved from one id into another)? We could use that to send out a message
# to other Bookwyrm instances to update their remote_id for the book, but ...how do we trigger any action?

View file

@ -40,11 +40,14 @@ class Notification(BookWyrmModel):
GROUP_NAME = "GROUP_NAME" GROUP_NAME = "GROUP_NAME"
GROUP_DESCRIPTION = "GROUP_DESCRIPTION" GROUP_DESCRIPTION = "GROUP_DESCRIPTION"
# Migrations
MOVE = "MOVE"
# pylint: disable=line-too-long # pylint: disable=line-too-long
NotificationType = models.TextChoices( NotificationType = models.TextChoices(
# there has got be a better way to do this # there has got be a better way to do this
"NotificationType", "NotificationType",
f"{FAVORITE} {REPLY} {MENTION} {TAG} {FOLLOW} {FOLLOW_REQUEST} {BOOST} {IMPORT} {ADD} {REPORT} {LINK_DOMAIN} {INVITE} {ACCEPT} {JOIN} {LEAVE} {REMOVE} {GROUP_PRIVACY} {GROUP_NAME} {GROUP_DESCRIPTION}", f"{FAVORITE} {REPLY} {MENTION} {TAG} {FOLLOW} {FOLLOW_REQUEST} {BOOST} {IMPORT} {ADD} {REPORT} {LINK_DOMAIN} {INVITE} {ACCEPT} {JOIN} {LEAVE} {REMOVE} {GROUP_PRIVACY} {GROUP_NAME} {GROUP_DESCRIPTION} {MOVE}",
) )
user = models.ForeignKey("User", on_delete=models.CASCADE) user = models.ForeignKey("User", on_delete=models.CASCADE)
@ -326,3 +329,14 @@ def notify_user_on_follow(sender, instance, created, *args, **kwargs):
notification_type=Notification.FOLLOW, notification_type=Notification.FOLLOW,
read=False, read=False,
) )
@receiver(models.signals.post_save, sender=Move)
# pylint: disable=unused-argument
def notify_on_move(sender, instance, *args, **kwargs):
"""someone moved something"""
Notification.notify(
instance.status.user,
instance.user,
related_object=instance.object,
notification_type=Notification.MOVE,
)

View file

@ -35,4 +35,6 @@
{% include 'notifications/items/update.html' %} {% include 'notifications/items/update.html' %}
{% elif notification.notification_type == 'GROUP_DESCRIPTION' %} {% elif notification.notification_type == 'GROUP_DESCRIPTION' %}
{% include 'notifications/items/update.html' %} {% include 'notifications/items/update.html' %}
{% elif notification.notification_type == 'MOVE' %}
{% include 'notifications/items/move.html' %}
{% endif %} {% endif %}

View file

@ -0,0 +1,28 @@
{% extends 'notifications/items/layout.html' %}
{% load i18n %}
{% load utilities %}
{% block primary_link %}{% spaceless %}
{{ notification.related_object.local_path }}
{% endspaceless %}{% endblock %}
{% block icon %}
<span class="icon icon-local"></span>
{% endblock %}
{% block description %}
<!--
TODO: a user has a 'name' but not everything does, notably a book.
On the other hand, maybe we don't need to notify anyone if a book
is moved, just update the remote_id?
-->
{% blocktrans trimmed with object_name=notification.related_object.name object_path=notification.related_object.local_path %}
<a href="{{ related_user_link }}">{{ related_user }}</a>
moved {{ object_name }}
"<a href="{{ object_path }}">{{ object_name }}</a>"
{% endblocktrans %}
<!-- TODO maybe put a brief context message here for migrated user accounts? -->
{% endblock %}