mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-10-31 22:19:00 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
dd8f91a044
9 changed files with 53 additions and 17 deletions
|
@ -55,7 +55,6 @@ class Work(Book):
|
|||
@dataclass(init=False)
|
||||
class Author(ActivityObject):
|
||||
''' author of a book '''
|
||||
url: str
|
||||
name: str
|
||||
born: str
|
||||
died: str
|
||||
|
|
|
@ -61,7 +61,7 @@ def broadcast_task(sender_id, activity, recipients):
|
|||
return errors
|
||||
|
||||
|
||||
def sign_and_send(sender, activity, destination):
|
||||
def sign_and_send(sender, data, destination):
|
||||
''' crpyto whatever and http junk '''
|
||||
now = http_date()
|
||||
|
||||
|
@ -69,7 +69,6 @@ def sign_and_send(sender, activity, destination):
|
|||
# this shouldn't happen. it would be bad if it happened.
|
||||
raise ValueError('No private key found for sender')
|
||||
|
||||
data = json.dumps(activity).encode('utf-8')
|
||||
digest = make_digest(data)
|
||||
|
||||
response = requests.post(
|
||||
|
|
|
@ -62,6 +62,8 @@ def shared_inbox(request):
|
|||
'Announce': handle_boost,
|
||||
'Add': {
|
||||
'Tag': handle_tag,
|
||||
'Edition': handle_shelve,
|
||||
'Work': handle_shelve,
|
||||
},
|
||||
'Undo': {
|
||||
'Follow': handle_unfollow,
|
||||
|
@ -318,6 +320,22 @@ def handle_tag(activity):
|
|||
status_builder.create_tag(user, book, activity['object']['name'])
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_shelve(activity):
|
||||
''' putting a book on a shelf '''
|
||||
user = get_or_create_remote_user(activity['actor'])
|
||||
book = books_manager.get_or_create_book(activity['object'])
|
||||
try:
|
||||
shelf = models.Shelf.objects.get(remote_id=activity['target'])
|
||||
except models.Shelf.DoesNotExist:
|
||||
return
|
||||
if shelf.user != user:
|
||||
# this doesn't add up.
|
||||
return
|
||||
shelf.books.add(book)
|
||||
shelf.save()
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_update_user(activity):
|
||||
''' receive an updated user Person activity object '''
|
||||
|
|
|
@ -68,7 +68,10 @@ class ActivitypubMixin:
|
|||
if not hasattr(self, mapping.model_key) or not mapping.activity_key:
|
||||
continue
|
||||
value = getattr(self, mapping.model_key)
|
||||
if hasattr(value, 'remote_id'):
|
||||
print(value)
|
||||
if hasattr(value, 'local_id'):
|
||||
value = value.local_id
|
||||
elif hasattr(value, 'remote_id'):
|
||||
value = value.remote_id
|
||||
if isinstance(value, datetime):
|
||||
value = value.isoformat()
|
||||
|
|
|
@ -56,7 +56,7 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
|||
@property
|
||||
def ap_authors(self):
|
||||
''' the activitypub serialization should be a list of author ids '''
|
||||
return [a.remote_id for a in self.authors.all()]
|
||||
return [a.local_id for a in self.authors.all()]
|
||||
|
||||
@property
|
||||
def ap_cover(self):
|
||||
|
@ -73,7 +73,7 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
|||
return self.parent_work.local_id
|
||||
|
||||
activity_mappings = [
|
||||
ActivityMapping('id', 'remote_id'),
|
||||
ActivityMapping('id', 'local_id'),
|
||||
|
||||
ActivityMapping('authors', 'ap_authors'),
|
||||
ActivityMapping('first_published_date', 'first_published_date'),
|
||||
|
@ -258,7 +258,7 @@ class Author(ActivitypubMixin, BookWyrmModel):
|
|||
an instance, so it needs a local url for federation. but it still needs
|
||||
the remote_id for easier deduplication and, if appropriate, to sync with
|
||||
the remote canonical copy (ditto here for author)'''
|
||||
return 'https://%s/book/%d' % (DOMAIN, self.id)
|
||||
return 'https://%s/author/%d' % (DOMAIN, self.id)
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
|
@ -271,8 +271,7 @@ class Author(ActivitypubMixin, BookWyrmModel):
|
|||
return self.last_name or self.first_name
|
||||
|
||||
activity_mappings = [
|
||||
ActivityMapping('id', 'remote_id'),
|
||||
ActivityMapping('url', 'remote_id'),
|
||||
ActivityMapping('id', 'local_id'),
|
||||
ActivityMapping('name', 'display_name'),
|
||||
ActivityMapping('born', 'born'),
|
||||
ActivityMapping('died', 'died'),
|
||||
|
|
|
@ -49,8 +49,8 @@ class ShelfBook(BookWyrmModel):
|
|||
return activitypub.Add(
|
||||
id='%s#add' % self.remote_id,
|
||||
actor=user.remote_id,
|
||||
object=self.book.to_activity(),
|
||||
target=self.shelf.to_activity()
|
||||
object=self.book.local_id,
|
||||
target=self.shelf.remote_id,
|
||||
).serialize()
|
||||
|
||||
def to_remove_activity(self, user):
|
||||
|
|
|
@ -44,7 +44,8 @@ def make_signature(sender, destination, date, digest):
|
|||
|
||||
def make_digest(data):
|
||||
''' creates a message digest for signing '''
|
||||
return 'SHA-256=' + b64encode(hashlib.sha256(data).digest()).decode('utf-8')
|
||||
return 'SHA-256=' + b64encode(hashlib.sha256(data.encode('utf-8'))\
|
||||
.digest()).decode('utf-8')
|
||||
|
||||
|
||||
def verify_digest(request):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>BookWyrm</title>
|
||||
<title>{% if title %}{{ title }} | {% endif %}BookWyrm</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/bulma.min.css">
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/format.css">
|
||||
|
|
|
@ -36,12 +36,12 @@ def is_api_request(request):
|
|||
|
||||
def server_error_page(request):
|
||||
''' 500 errors '''
|
||||
return TemplateResponse(request, 'error.html')
|
||||
return TemplateResponse(request, 'error.html', {'title': 'Oops!'})
|
||||
|
||||
|
||||
def not_found_page(request, _):
|
||||
''' 404s '''
|
||||
return TemplateResponse(request, 'notfound.html')
|
||||
return TemplateResponse(request, 'notfound.html', {'title': 'Not found'})
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -97,6 +97,7 @@ def home_tab(request, tab):
|
|||
next_page = '/?page=%d#feed' % (page + 1)
|
||||
prev_page = '/?page=%d#feed' % (page - 1)
|
||||
data = {
|
||||
'title': 'Updates Feed',
|
||||
'user': request.user,
|
||||
'suggested_books': set(suggested_books),
|
||||
'activities': activities,
|
||||
|
@ -181,6 +182,7 @@ def search(request):
|
|||
|
||||
book_results = books_manager.search(query)
|
||||
data = {
|
||||
'title': 'Search Results',
|
||||
'book_results': book_results,
|
||||
'user_results': user_results,
|
||||
'query': query,
|
||||
|
@ -192,6 +194,7 @@ def search(request):
|
|||
def import_page(request):
|
||||
''' import history from goodreads '''
|
||||
return TemplateResponse(request, 'import.html', {
|
||||
'title': 'Import Books',
|
||||
'import_form': forms.ImportForm(),
|
||||
'jobs': models.ImportJob.
|
||||
objects.filter(user=request.user).order_by('-created_date'),
|
||||
|
@ -207,6 +210,7 @@ def import_status(request, job_id):
|
|||
raise PermissionDenied
|
||||
task = app.AsyncResult(job.task_id)
|
||||
return TemplateResponse(request, 'import_status.html', {
|
||||
'title': 'Import Status',
|
||||
'job': job,
|
||||
'items': job.items.order_by('index').all(),
|
||||
'task': task
|
||||
|
@ -219,6 +223,7 @@ def login_page(request):
|
|||
return redirect('/')
|
||||
# send user to the login page
|
||||
data = {
|
||||
'title': 'Login',
|
||||
'site_settings': models.SiteSettings.get(),
|
||||
'login_form': forms.LoginForm(),
|
||||
'register_form': forms.RegisterForm(),
|
||||
|
@ -229,6 +234,7 @@ def login_page(request):
|
|||
def about_page(request):
|
||||
''' more information about the instance '''
|
||||
data = {
|
||||
'title': 'About',
|
||||
'site_settings': models.SiteSettings.get(),
|
||||
}
|
||||
return TemplateResponse(request, 'about.html', data)
|
||||
|
@ -236,7 +242,7 @@ def about_page(request):
|
|||
|
||||
def password_reset_request(request):
|
||||
''' invite management page '''
|
||||
return TemplateResponse(request, 'password_reset_request.html')
|
||||
return TemplateResponse(request, 'password_reset_request.html', {'title': 'Reset Password'})
|
||||
|
||||
|
||||
def password_reset(request, code):
|
||||
|
@ -253,7 +259,7 @@ def password_reset(request, code):
|
|||
return TemplateResponse(
|
||||
request,
|
||||
'password_reset.html',
|
||||
{'code': reset_code.code}
|
||||
{'title': 'Reset Password', 'code': reset_code.code}
|
||||
)
|
||||
|
||||
|
||||
|
@ -269,6 +275,7 @@ def invite_page(request, code):
|
|||
raise PermissionDenied
|
||||
|
||||
data = {
|
||||
'title': 'Join',
|
||||
'site_settings': models.SiteSettings.get(),
|
||||
'register_form': forms.RegisterForm(),
|
||||
'invite': invite,
|
||||
|
@ -280,6 +287,7 @@ def invite_page(request, code):
|
|||
def manage_invites(request):
|
||||
''' invite management page '''
|
||||
data = {
|
||||
'title': 'Invitations',
|
||||
'invites': models.SiteInvite.objects.filter(user=request.user),
|
||||
'form': forms.CreateInviteForm(),
|
||||
}
|
||||
|
@ -293,6 +301,7 @@ def notifications_page(request):
|
|||
.order_by('-created_date')
|
||||
unread = [n.id for n in notifications.filter(read=False)]
|
||||
data = {
|
||||
'title': 'Notifications',
|
||||
'notifications': notifications,
|
||||
'unread': unread,
|
||||
}
|
||||
|
@ -313,6 +322,7 @@ def user_page(request, username, subpage=None, shelf=None):
|
|||
# otherwise we're at a UI view
|
||||
|
||||
data = {
|
||||
'title': user.name,
|
||||
'user': user,
|
||||
'is_self': request.user.id == user.id,
|
||||
}
|
||||
|
@ -416,6 +426,7 @@ def status_page(request, username, status_id):
|
|||
return JsonResponse(status.to_activity(), encoder=ActivityEncoder)
|
||||
|
||||
data = {
|
||||
'title': status.type,
|
||||
'status': status,
|
||||
}
|
||||
return TemplateResponse(request, 'status.html', data)
|
||||
|
@ -460,6 +471,7 @@ def edit_profile_page(request):
|
|||
|
||||
form = forms.EditUserForm(instance=request.user)
|
||||
data = {
|
||||
'title': 'Edit profile',
|
||||
'form': form,
|
||||
'user': user,
|
||||
}
|
||||
|
@ -506,6 +518,7 @@ def book_page(request, book_id):
|
|||
).distinct().all()
|
||||
|
||||
data = {
|
||||
'title': book.title,
|
||||
'book': book,
|
||||
'reviews': reviews.filter(content__isnull=False),
|
||||
'ratings': reviews.filter(content__isnull=True),
|
||||
|
@ -539,6 +552,7 @@ def edit_book_page(request, book_id):
|
|||
if not book.description:
|
||||
book.description = book.parent_work.description
|
||||
data = {
|
||||
'title': 'Edit Book',
|
||||
'book': book,
|
||||
'form': forms.EditionForm(instance=book)
|
||||
}
|
||||
|
@ -550,6 +564,7 @@ def editions_page(request, work_id):
|
|||
work = models.Work.objects.get(id=work_id)
|
||||
editions = models.Edition.objects.filter(parent_work=work).all()
|
||||
data = {
|
||||
'title': 'Editions of %s' % work.title,
|
||||
'editions': editions,
|
||||
'work': work,
|
||||
}
|
||||
|
@ -568,6 +583,7 @@ def author_page(request, author_id):
|
|||
|
||||
books = models.Work.objects.filter(authors=author)
|
||||
data = {
|
||||
'title': author.name,
|
||||
'author': author,
|
||||
'books': [b.default_edition for b in books],
|
||||
}
|
||||
|
@ -586,6 +602,7 @@ def tag_page(request, tag_id):
|
|||
|
||||
books = models.Edition.objects.filter(tag__identifier=tag_id).distinct()
|
||||
data = {
|
||||
'title': tag_obj.name,
|
||||
'books': books,
|
||||
'tag': tag_obj,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue