Delete statuses

This commit is contained in:
Mouse Reeve 2020-10-08 12:32:45 -07:00
parent 1705a550d4
commit 704e1092c4
8 changed files with 71 additions and 2 deletions

View file

@ -4,6 +4,7 @@ import sys
from .base_activity import ActivityEncoder, Image, PublicKey, Signature from .base_activity import ActivityEncoder, Image, PublicKey, Signature
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
from .note import Tombstone
from .interaction import Boost, Like from .interaction import Boost, Like
from .ordered_collection import OrderedCollection, OrderedCollectionPage from .ordered_collection import OrderedCollection, OrderedCollectionPage
from .person import Person from .person import Person

View file

@ -4,6 +4,14 @@ from typing import Dict, List
from .base_activity import ActivityObject, Image from .base_activity import ActivityObject, Image
@dataclass(init=False)
class Tombstone(ActivityObject):
url: str
published: str
deleted: str
type: str = 'Tombstone'
@dataclass(init=False) @dataclass(init=False)
class Note(ActivityObject): class Note(ActivityObject):
''' Note activity ''' ''' Note activity '''

View file

@ -22,6 +22,8 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
sensitive = models.BooleanField(default=False) sensitive = models.BooleanField(default=False)
# the created date can't be this, because of receiving federated posts # the created date can't be this, because of receiving federated posts
published_date = models.DateTimeField(default=timezone.now) published_date = models.DateTimeField(default=timezone.now)
deleted = models.BooleanField(default=False)
deleted_date = models.DateTimeField(default=timezone.now)
favorites = models.ManyToManyField( favorites = models.ManyToManyField(
'User', 'User',
symmetrical=False, symmetrical=False,
@ -104,6 +106,18 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
**kwargs **kwargs
) )
def to_activity(self, **kwargs):
''' return tombstone if the status is deleted '''
if self.deleted:
return activitypub.Tombstone(
id=self.remote_id,
url=self.remote_id,
deleted=http_date(self.deleted_date.timestamp()),
published=http_date(self.deleted_date.timestamp()),
).serialize()
return ActivitypubMixin.to_activity(self, **kwargs)
class GeneratedStatus(Status): class GeneratedStatus(Status):
''' these are app-generated messages about user activity ''' ''' these are app-generated messages about user activity '''
@property @property
@ -112,7 +126,7 @@ class GeneratedStatus(Status):
message = self.content message = self.content
books = ', '.join( books = ', '.join(
'<a href="%s">"%s"</a>' % (self.book.local_id, self.book.title) \ '<a href="%s">"%s"</a>' % (self.book.local_id, self.book.title) \
for book in self.mention_books for book in self.mention_books.all()
) )
return '%s %s' % (message, books) return '%s %s' % (message, books)

View file

@ -13,6 +13,7 @@ from bookwyrm.status import create_review, create_status
from bookwyrm.status import create_quotation, create_comment from bookwyrm.status import create_quotation, create_comment
from bookwyrm.status import create_tag, create_notification, create_rating from bookwyrm.status import create_tag, create_notification, create_rating
from bookwyrm.status import create_generated_note from bookwyrm.status import create_generated_note
from bookwyrm.status import delete_status
from bookwyrm.remote_user import get_or_create_remote_user from bookwyrm.remote_user import get_or_create_remote_user
@ -197,6 +198,12 @@ def handle_import_books(user, items):
return None return None
def handle_delete_status(user, status):
''' delete a status and broadcast deletion to other servers '''
delete_status(status)
broadcast(user, status.to_activity())
def handle_rate(user, book, rating): def handle_rate(user, book, rating):
''' a review that's just a rating ''' ''' a review that's just a rating '''
builder = create_rating builder = create_rating

View file

@ -6,6 +6,11 @@ from bookwyrm.books_manager import get_or_create_book
from bookwyrm.sanitize_html import InputHtmlParser from bookwyrm.sanitize_html import InputHtmlParser
def delete_status(status):
''' replace the status with a tombstone '''
status.deleted = True
status.save()
def create_rating(user, book, rating): def create_rating(user, book, rating):
''' a review that's just a rating ''' ''' a review that's just a rating '''
if not rating or rating < 1 or rating > 5: if not rating or rating < 1 or rating > 5:

View file

@ -25,6 +25,17 @@
<span class="icon icon-public"> <span class="icon icon-public">
<span class="is-sr-only">Public post</span> <span class="is-sr-only">Public post</span>
</span> </span>
{% if status.user == request.user %}
<form name="delete-{{status.id}}" action="/delete-status" method="post">
{% csrf_token %}
<input type="hidden" name="status" value="{{ status.id }}">
<button type="submit">
<span class="icon icon-cancel">
<span class="is-sr-only">Delete post</span>
</span>
</button>
</form>
{% endif %}
<a href="{{ status.remote_id }}">{{ status.published_date | naturaltime }}</a> <a href="{{ status.remote_id }}">{{ status.published_date | naturaltime }}</a>
</div> </div>
</footer> </footer>

View file

@ -11,7 +11,7 @@ localname_regex = r'(?P<username>[\w\-_]+)'
user_path = r'^user/%s' % username_regex user_path = r'^user/%s' % username_regex
local_user_path = r'^user/%s' % localname_regex local_user_path = r'^user/%s' % localname_regex
status_types = ['status', 'review', 'comment', 'quotation', 'boost'] status_types = ['status', 'review', 'comment', 'quotation', 'boost', 'generatedstatus']
status_path = r'%s/(%s)/(?P<status_id>\d+)' % \ status_path = r'%s/(%s)/(?P<status_id>\d+)' % \
(local_user_path, '|'.join(status_types)) (local_user_path, '|'.join(status_types))
@ -107,6 +107,8 @@ urlpatterns = [
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite), re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
re_path(r'^boost/(?P<status_id>\d+)/?$', actions.boost), re_path(r'^boost/(?P<status_id>\d+)/?$', actions.boost),
re_path(r'^delete-status/?$', actions.delete_status),
re_path(r'^shelve/?$', actions.shelve), re_path(r'^shelve/?$', actions.shelve),
re_path(r'^follow/?$', actions.follow), re_path(r'^follow/?$', actions.follow),

View file

@ -418,6 +418,27 @@ def boost(request, status_id):
outgoing.handle_boost(request.user, status) outgoing.handle_boost(request.user, status)
return redirect(request.headers.get('Referer', '/')) return redirect(request.headers.get('Referer', '/'))
@login_required
def delete_status(request):
''' delete and tombstone a status '''
status_id = request.POST.get('status')
if not status_id:
return HttpResponseBadRequest()
try:
status = models.Status.objects.get(id=status_id)
except models.Status.DoesNotExist:
return HttpResponseBadRequest()
# don't let people delete other people's statuses
if status.user != request.user:
return HttpResponseBadRequest()
# perform deletion
outgoing.handle_delete_status(request.user, status)
return redirect(request.headers.get('Referer', '/'))
@login_required @login_required
def follow(request): def follow(request):
''' follow another user, here or abroad ''' ''' follow another user, here or abroad '''