Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2020-10-27 12:18:46 -07:00
commit 7dd1deb438
12 changed files with 78 additions and 25 deletions

View file

@ -44,10 +44,9 @@ class ActivityObject:
type: str type: str
def __init__(self, **kwargs): def __init__(self, **kwargs):
''' this lets you pass in an object with fields ''' this lets you pass in an object with fields that aren't in the
that aren't in the dataclass, which it ignores. dataclass, which it ignores. Any field in the dataclass is required or
Any field in the dataclass is required or has a has a default value '''
default value '''
for field in fields(self): for field in fields(self):
try: try:
value = kwargs[field.name] value = kwargs[field.name]
@ -59,7 +58,7 @@ class ActivityObject:
def to_model(self, model, instance=None): def to_model(self, model, instance=None):
''' convert from an activity to a model ''' ''' convert from an activity to a model instance '''
if not isinstance(self, model.activity_serializer): if not isinstance(self, model.activity_serializer):
raise TypeError('Wrong activity type for model') raise TypeError('Wrong activity type for model')

View file

@ -52,7 +52,6 @@ class Work(Book):
type: str = 'Work' type: str = 'Work'
@dataclass(init=False) @dataclass(init=False)
class Author(ActivityObject): class Author(ActivityObject):
''' author of a book ''' ''' author of a book '''

View file

@ -6,6 +6,7 @@ from .base_activity import ActivityObject, Image
@dataclass(init=False) @dataclass(init=False)
class Tombstone(ActivityObject): class Tombstone(ActivityObject):
''' the placeholder for a deleted status '''
url: str url: str
published: str published: str
deleted: str deleted: str
@ -23,7 +24,6 @@ class Note(ActivityObject):
cc: List[str] cc: List[str]
content: str content: str
replies: Dict replies: Dict
# TODO: this is wrong???
attachment: List[Image] = field(default=lambda: []) attachment: List[Image] = field(default=lambda: [])
sensitive: bool = False sensitive: bool = False
type: str = 'Note' type: str = 'Note'

View file

@ -13,7 +13,6 @@ def get_public_recipients(user, software=None):
''' everybody and their public inboxes ''' ''' everybody and their public inboxes '''
followers = user.followers.filter(local=False) followers = user.followers.filter(local=False)
if software: if software:
# TODO: eventually we may want to handle particular software differently
followers = followers.filter(bookwyrm_user=(software == 'bookwyrm')) followers = followers.filter(bookwyrm_user=(software == 'bookwyrm'))
# we want shared inboxes when available # we want shared inboxes when available
@ -36,7 +35,6 @@ def broadcast(sender, activity, software=None, \
# start with parsing the direct recipients # start with parsing the direct recipients
recipients = [u.inbox for u in direct_recipients or []] recipients = [u.inbox for u in direct_recipients or []]
# and then add any other recipients # and then add any other recipients
# TODO: other kinds of privacy
if privacy == 'public': if privacy == 'public':
recipients += get_public_recipients(sender, software=software) recipients += get_public_recipients(sender, software=software)
broadcast_task.delay( broadcast_task.delay(
@ -55,7 +53,6 @@ def broadcast_task(sender_id, activity, recipients):
try: try:
sign_and_send(sender, activity, recipient) sign_and_send(sender, activity, recipient)
except requests.exceptions.HTTPError as e: except requests.exceptions.HTTPError as e:
# TODO: maybe keep track of users who cause errors
errors.append({ errors.append({
'error': str(e), 'error': str(e),
'recipient': recipient, 'recipient': recipient,

View file

@ -6,7 +6,6 @@ from bookwyrm.tasks import app
def password_reset_email(reset_code): def password_reset_email(reset_code):
''' generate a password reset email ''' ''' generate a password reset email '''
# TODO; this should be tempalted
site = models.SiteSettings.get() site = models.SiteSettings.get()
send_email.delay( send_email.delay(
reset_code.user.email, reset_code.user.email,

View file

@ -68,7 +68,7 @@ def shared_inbox(request):
'Like': handle_unfavorite, 'Like': handle_unfavorite,
}, },
'Update': { 'Update': {
'Person': None,# TODO: handle_update_user 'Person': handle_update_user,
'Document': handle_update_book, 'Document': handle_update_book,
}, },
} }
@ -308,6 +308,20 @@ def handle_tag(activity):
status_builder.create_tag(user, book, activity['object']['name']) status_builder.create_tag(user, book, activity['object']['name'])
@app.task
def handle_update_user(activity):
''' receive an updated user Person activity object '''
try:
user = models.User.objects.get(remote_id=activity['object']['id'])
except models.User.DoesNotExist:
# who is this person? who cares
return
activitypub.Person(
**activity['object']
).to_model(models.User, instance=user)
# model save() happens in the to_model function
@app.task @app.task
def handle_update_book(activity): def handle_update_book(activity):
''' a remote instance changed a book (Document) ''' ''' a remote instance changed a book (Document) '''

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.7 on 2020-10-21 01:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bookwyrm', '0055_merge_20201017_0011'),
]
operations = [
migrations.AlterField(
model_name='status',
name='deleted_date',
field=models.DateTimeField(blank=True, null=True),
),
]

View file

@ -23,7 +23,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
# 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 = models.BooleanField(default=False)
deleted_date = models.DateTimeField() deleted_date = models.DateTimeField(blank=True, null=True)
favorites = models.ManyToManyField( favorites = models.ManyToManyField(
'User', 'User',
symmetrical=False, symmetrical=False,

View file

@ -15,9 +15,6 @@
<div class="control"> <div class="control">
{{ login_form.username }} {{ login_form.username }}
</div> </div>
{% for error in login_form.username.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="id_password">Password:</label> <label class="label" for="id_password">Password:</label>

View file

@ -0,0 +1,30 @@
import json
import pathlib
from django.test import TestCase
from bookwyrm import models, incoming
class UpdateUser(TestCase):
def setUp(self):
self.user = models.User.objects.create_user(
'mouse', 'mouse@mouse.com', 'mouseword',
remote_id='https://example.com/user/mouse',
local=False,
localname='mouse'
)
datafile = pathlib.Path(__file__).parent.joinpath(
'../data/ap_user.json'
)
self.user_data = json.loads(datafile.read_bytes())
def test_handle_update_user(self):
self.assertIsNone(self.user.name)
self.assertEqual(self.user.localname, 'mouse')
incoming.handle_update_user({'object': self.user_data})
self.user = models.User.objects.get(id=self.user.id)
self.assertEqual(self.user.name, 'MOUSE?? MOUSE!!')
self.assertEqual(self.user.localname, 'mouse')

View file

@ -11,7 +11,14 @@ 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', 'generatedstatus'] 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))

View file

@ -24,14 +24,6 @@ def user_login(request):
return redirect('/login') return redirect('/login')
login_form = forms.LoginForm(request.POST) login_form = forms.LoginForm(request.POST)
register_form = forms.RegisterForm()
if not login_form.is_valid():
data = {
'site_settings': models.SiteSettings.get(),
'login_form': login_form,
'register_form': register_form
}
return TemplateResponse(request, 'login.html', data)
username = login_form.data['username'] username = login_form.data['username']
username = '%s@%s' % (username, DOMAIN) username = '%s@%s' % (username, DOMAIN)
@ -42,6 +34,7 @@ def user_login(request):
return redirect(request.GET.get('next', '/')) return redirect(request.GET.get('next', '/'))
login_form.non_field_errors = 'Username or password are incorrect' login_form.non_field_errors = 'Username or password are incorrect'
register_form = forms.RegisterForm()
data = { data = {
'site_settings': models.SiteSettings.get(), 'site_settings': models.SiteSettings.get(),
'login_form': login_form, 'login_form': login_form,