mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-26 19:41:11 +00:00
commit
1672c699e5
14 changed files with 155 additions and 5 deletions
|
@ -4,11 +4,12 @@ import sys
|
|||
|
||||
from .base_activity import ActivityEncoder, Image, PublicKey, Signature
|
||||
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
|
||||
from .note import Tombstone
|
||||
from .interaction import Boost, Like
|
||||
from .ordered_collection import OrderedCollection, OrderedCollectionPage
|
||||
from .person import Person
|
||||
from .book import Edition, Work, Author
|
||||
from .verbs import Create, Undo, Update
|
||||
from .verbs import Create, Delete, Undo, Update
|
||||
from .verbs import Follow, Accept, Reject
|
||||
from .verbs import Add, Remove
|
||||
|
||||
|
|
|
@ -4,6 +4,14 @@ from typing import Dict, List
|
|||
|
||||
from .base_activity import ActivityObject, Image
|
||||
|
||||
@dataclass(init=False)
|
||||
class Tombstone(ActivityObject):
|
||||
url: str
|
||||
published: str
|
||||
deleted: str
|
||||
type: str = 'Tombstone'
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class Note(ActivityObject):
|
||||
''' Note activity '''
|
||||
|
|
|
@ -21,6 +21,15 @@ class Create(Verb):
|
|||
type: str = 'Create'
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class Delete(Verb):
|
||||
''' Create activity '''
|
||||
to: List
|
||||
cc: List
|
||||
signature: Signature
|
||||
type: str = 'Delete'
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class Update(Verb):
|
||||
''' Update activity '''
|
||||
|
|
|
@ -57,6 +57,7 @@ def shared_inbox(request):
|
|||
'Accept': handle_follow_accept,
|
||||
'Reject': handle_follow_reject,
|
||||
'Create': handle_create,
|
||||
'Delete': handle_delete_status,
|
||||
'Like': handle_favorite,
|
||||
'Announce': handle_boost,
|
||||
'Add': {
|
||||
|
@ -237,6 +238,20 @@ def handle_create(activity):
|
|||
)
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_delete_status(activity):
|
||||
''' remove a status '''
|
||||
status_id = activity['object']['id']
|
||||
try:
|
||||
status = models.Status.objects.select_subclasses().get(
|
||||
remote_id=status_id
|
||||
)
|
||||
except models.Status.DoesNotExist:
|
||||
return
|
||||
status_builder.delete_status(status)
|
||||
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_favorite(activity):
|
||||
''' approval of your good good post '''
|
||||
|
|
24
bookwyrm/migrations/0053_auto_20201006_2020.py
Normal file
24
bookwyrm/migrations/0053_auto_20201006_2020.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 3.0.7 on 2020-10-06 20:20
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bookwyrm', '0052_auto_20201005_2145'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='status',
|
||||
name='deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='status',
|
||||
name='deleted_date',
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
]
|
18
bookwyrm/migrations/0054_auto_20201016_2359.py
Normal file
18
bookwyrm/migrations/0054_auto_20201016_2359.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.0.7 on 2020-10-16 23:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bookwyrm', '0053_auto_20201006_2020'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='status',
|
||||
name='deleted_date',
|
||||
field=models.DateTimeField(),
|
||||
),
|
||||
]
|
|
@ -22,6 +22,8 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
sensitive = models.BooleanField(default=False)
|
||||
# the created date can't be this, because of receiving federated posts
|
||||
published_date = models.DateTimeField(default=timezone.now)
|
||||
deleted = models.BooleanField(default=False)
|
||||
deleted_date = models.DateTimeField()
|
||||
favorites = models.ManyToManyField(
|
||||
'User',
|
||||
symmetrical=False,
|
||||
|
@ -104,6 +106,18 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
**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):
|
||||
''' these are app-generated messages about user activity '''
|
||||
@property
|
||||
|
@ -112,7 +126,7 @@ class GeneratedStatus(Status):
|
|||
message = self.content
|
||||
books = ', '.join(
|
||||
'<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)
|
||||
|
||||
|
|
|
@ -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_tag, create_notification, create_rating
|
||||
from bookwyrm.status import create_generated_note
|
||||
from bookwyrm.status import delete_status
|
||||
from bookwyrm.remote_user import get_or_create_remote_user
|
||||
|
||||
|
||||
|
@ -202,6 +203,12 @@ def handle_import_books(user, items):
|
|||
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):
|
||||
''' a review that's just a rating '''
|
||||
builder = create_rating
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
''' Handle user activity '''
|
||||
from datetime import datetime
|
||||
from django.db import IntegrityError
|
||||
|
||||
from bookwyrm import models
|
||||
|
@ -6,6 +7,12 @@ from bookwyrm.books_manager import get_or_create_book
|
|||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
|
||||
|
||||
def delete_status(status):
|
||||
''' replace the status with a tombstone '''
|
||||
status.deleted = True
|
||||
status.deleted_date = datetime.now()
|
||||
status.save()
|
||||
|
||||
def create_rating(user, book, rating):
|
||||
''' a review that's just a rating '''
|
||||
if not rating or rating < 1 or rating > 5:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% load humanize %}
|
||||
{% load fr_display %}
|
||||
|
||||
{% if not status.deleted %}
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
{% include 'snippets/status_header.html' with status=status %}
|
||||
|
@ -25,7 +26,29 @@
|
|||
<span class="icon icon-public">
|
||||
<span class="is-sr-only">Public post</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>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p>
|
||||
{% include 'snippets/avatar.html' with user=status.user %}
|
||||
{% include 'snippets/username.html' with user=status.user %}
|
||||
deleted this status
|
||||
</p>
|
||||
</header>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -42,7 +42,8 @@ def get_replies(status):
|
|||
''' get all direct replies to a status '''
|
||||
#TODO: this limit could cause problems
|
||||
return models.Status.objects.filter(
|
||||
reply_parent=status
|
||||
reply_parent=status,
|
||||
deleted=False,
|
||||
).select_subclasses().all()[:10]
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ localname_regex = r'(?P<username>[\w\-_]+)'
|
|||
user_path = r'^user/%s' % username_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+)' % \
|
||||
(local_user_path, '|'.join(status_types))
|
||||
|
||||
|
@ -107,6 +107,8 @@ urlpatterns = [
|
|||
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
||||
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'^follow/?$', actions.follow),
|
||||
|
|
|
@ -418,6 +418,27 @@ def boost(request, status_id):
|
|||
outgoing.handle_boost(request.user, status)
|
||||
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
|
||||
def follow(request):
|
||||
''' follow another user, here or abroad '''
|
||||
|
|
|
@ -118,7 +118,7 @@ def get_activity_feed(user, filter_level, model=models.Status):
|
|||
|
||||
activities = model
|
||||
if hasattr(model, 'objects'):
|
||||
activities = model.objects
|
||||
activities = model.objects.filter(deleted=False)
|
||||
|
||||
activities = activities.order_by(
|
||||
'-created_date'
|
||||
|
|
Loading…
Reference in a new issue