forked from mirrors/bookwyrm
Removes outgoing and view_actions
This commit is contained in:
parent
a385aa4cb5
commit
3e5ed19643
8 changed files with 199 additions and 169 deletions
|
@ -2,10 +2,11 @@
|
||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from bookwyrm import outgoing
|
from bookwyrm import models
|
||||||
from bookwyrm.tasks import app
|
from bookwyrm.broadcast import broadcast
|
||||||
from bookwyrm.models import ImportJob, ImportItem
|
from bookwyrm.models import ImportJob, ImportItem
|
||||||
from bookwyrm.status import create_notification
|
from bookwyrm.status import create_notification
|
||||||
|
from bookwyrm.tasks import app
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ def import_data(job_id):
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
# shelves book and handles reviews
|
# shelves book and handles reviews
|
||||||
outgoing.handle_imported_book(
|
handle_imported_book(
|
||||||
job.user, item, job.include_reviews, job.privacy)
|
job.user, item, job.include_reviews, job.privacy)
|
||||||
else:
|
else:
|
||||||
item.fail_reason = 'Could not find a match for book'
|
item.fail_reason = 'Could not find a match for book'
|
||||||
|
@ -71,3 +72,57 @@ def import_data(job_id):
|
||||||
create_notification(job.user, 'IMPORT', related_import=job)
|
create_notification(job.user, 'IMPORT', related_import=job)
|
||||||
job.complete = True
|
job.complete = True
|
||||||
job.save()
|
job.save()
|
||||||
|
|
||||||
|
|
||||||
|
def handle_imported_book(user, item, include_reviews, privacy):
|
||||||
|
''' process a goodreads csv and then post about it '''
|
||||||
|
if isinstance(item.book, models.Work):
|
||||||
|
item.book = item.book.default_edition
|
||||||
|
if not item.book:
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_shelf = models.ShelfBook.objects.filter(
|
||||||
|
book=item.book, added_by=user).exists()
|
||||||
|
|
||||||
|
# shelve the book if it hasn't been shelved already
|
||||||
|
if item.shelf and not existing_shelf:
|
||||||
|
desired_shelf = models.Shelf.objects.get(
|
||||||
|
identifier=item.shelf,
|
||||||
|
user=user
|
||||||
|
)
|
||||||
|
shelf_book = models.ShelfBook.objects.create(
|
||||||
|
book=item.book, shelf=desired_shelf, added_by=user)
|
||||||
|
broadcast(user, shelf_book.to_add_activity(user), privacy=privacy)
|
||||||
|
|
||||||
|
for read in item.reads:
|
||||||
|
# check for an existing readthrough with the same dates
|
||||||
|
if models.ReadThrough.objects.filter(
|
||||||
|
user=user, book=item.book,
|
||||||
|
start_date=read.start_date,
|
||||||
|
finish_date=read.finish_date
|
||||||
|
).exists():
|
||||||
|
continue
|
||||||
|
read.book = item.book
|
||||||
|
read.user = user
|
||||||
|
read.save()
|
||||||
|
|
||||||
|
if include_reviews and (item.rating or item.review):
|
||||||
|
review_title = 'Review of {!r} on Goodreads'.format(
|
||||||
|
item.book.title,
|
||||||
|
) if item.review else ''
|
||||||
|
|
||||||
|
# we don't know the publication date of the review,
|
||||||
|
# but "now" is a bad guess
|
||||||
|
published_date_guess = item.date_read or item.date_added
|
||||||
|
review = models.Review.objects.create(
|
||||||
|
user=user,
|
||||||
|
book=item.book,
|
||||||
|
name=review_title,
|
||||||
|
content=item.review,
|
||||||
|
rating=item.rating,
|
||||||
|
published_date=published_date_guess,
|
||||||
|
privacy=privacy,
|
||||||
|
)
|
||||||
|
# we don't need to send out pure activities because non-bookwyrm
|
||||||
|
# instances don't need this data
|
||||||
|
broadcast(user, review.to_create_activity(user), privacy=privacy)
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from bookwyrm import activitypub, models, outgoing
|
from bookwyrm import activitypub, models, views
|
||||||
from bookwyrm import status as status_builder
|
from bookwyrm import status as status_builder
|
||||||
from bookwyrm.tasks import app
|
from bookwyrm.tasks import app
|
||||||
from bookwyrm.signatures import Signature
|
from bookwyrm.signatures import Signature
|
||||||
|
@ -133,7 +133,7 @@ def handle_follow(activity):
|
||||||
related_user=relationship.user_subject
|
related_user=relationship.user_subject
|
||||||
)
|
)
|
||||||
if not manually_approves:
|
if not manually_approves:
|
||||||
outgoing.handle_accept(relationship)
|
views.handle_accept(relationship)
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
''' handles all the activity coming out of the server '''
|
|
||||||
from django.db import transaction
|
|
||||||
from django.http import JsonResponse
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
|
||||||
from django.views.decorators.http import require_GET
|
|
||||||
from requests import HTTPError
|
|
||||||
|
|
||||||
from bookwyrm import activitypub
|
|
||||||
from bookwyrm import models
|
|
||||||
from bookwyrm.connectors import get_data, ConnectorException
|
|
||||||
from bookwyrm.broadcast import broadcast
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_GET
|
|
||||||
def outbox(request, username):
|
|
||||||
''' outbox for the requested user '''
|
|
||||||
user = get_object_or_404(models.User, localname=username)
|
|
||||||
filter_type = request.GET.get('type')
|
|
||||||
if filter_type not in models.status_models:
|
|
||||||
filter_type = None
|
|
||||||
|
|
||||||
return JsonResponse(
|
|
||||||
user.to_outbox(**request.GET, filter_type=filter_type),
|
|
||||||
encoder=activitypub.ActivityEncoder
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_remote_webfinger(query):
|
|
||||||
''' webfingerin' other servers '''
|
|
||||||
user = None
|
|
||||||
|
|
||||||
# usernames could be @user@domain or user@domain
|
|
||||||
if not query:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if query[0] == '@':
|
|
||||||
query = query[1:]
|
|
||||||
|
|
||||||
try:
|
|
||||||
domain = query.split('@')[1]
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = models.User.objects.get(username=query)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
url = 'https://%s/.well-known/webfinger?resource=acct:%s' % \
|
|
||||||
(domain, query)
|
|
||||||
try:
|
|
||||||
data = get_data(url)
|
|
||||||
except (ConnectorException, HTTPError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
for link in data.get('links'):
|
|
||||||
if link.get('rel') == 'self':
|
|
||||||
try:
|
|
||||||
user = activitypub.resolve_remote_id(
|
|
||||||
models.User, link['href']
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
return None
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
def handle_follow(user, to_follow):
|
|
||||||
''' someone local wants to follow someone '''
|
|
||||||
relationship, _ = models.UserFollowRequest.objects.get_or_create(
|
|
||||||
user_subject=user,
|
|
||||||
user_object=to_follow,
|
|
||||||
)
|
|
||||||
activity = relationship.to_activity()
|
|
||||||
broadcast(user, activity, privacy='direct', direct_recipients=[to_follow])
|
|
||||||
|
|
||||||
|
|
||||||
def handle_unfollow(user, to_unfollow):
|
|
||||||
''' someone local wants to follow someone '''
|
|
||||||
relationship = models.UserFollows.objects.get(
|
|
||||||
user_subject=user,
|
|
||||||
user_object=to_unfollow
|
|
||||||
)
|
|
||||||
activity = relationship.to_undo_activity(user)
|
|
||||||
broadcast(user, activity, privacy='direct', direct_recipients=[to_unfollow])
|
|
||||||
to_unfollow.followers.remove(user)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_accept(follow_request):
|
|
||||||
''' send an acceptance message to a follow request '''
|
|
||||||
user = follow_request.user_subject
|
|
||||||
to_follow = follow_request.user_object
|
|
||||||
with transaction.atomic():
|
|
||||||
relationship = models.UserFollows.from_request(follow_request)
|
|
||||||
follow_request.delete()
|
|
||||||
relationship.save()
|
|
||||||
|
|
||||||
activity = relationship.to_accept_activity()
|
|
||||||
broadcast(to_follow, activity, privacy='direct', direct_recipients=[user])
|
|
||||||
|
|
||||||
|
|
||||||
def handle_reject(follow_request):
|
|
||||||
''' a local user who managed follows rejects a follow request '''
|
|
||||||
user = follow_request.user_subject
|
|
||||||
to_follow = follow_request.user_object
|
|
||||||
activity = follow_request.to_reject_activity()
|
|
||||||
follow_request.delete()
|
|
||||||
broadcast(to_follow, activity, privacy='direct', direct_recipients=[user])
|
|
||||||
|
|
||||||
|
|
||||||
def handle_imported_book(user, item, include_reviews, privacy):
|
|
||||||
''' process a goodreads csv and then post about it '''
|
|
||||||
if isinstance(item.book, models.Work):
|
|
||||||
item.book = item.book.default_edition
|
|
||||||
if not item.book:
|
|
||||||
return
|
|
||||||
|
|
||||||
existing_shelf = models.ShelfBook.objects.filter(
|
|
||||||
book=item.book, added_by=user).exists()
|
|
||||||
|
|
||||||
# shelve the book if it hasn't been shelved already
|
|
||||||
if item.shelf and not existing_shelf:
|
|
||||||
desired_shelf = models.Shelf.objects.get(
|
|
||||||
identifier=item.shelf,
|
|
||||||
user=user
|
|
||||||
)
|
|
||||||
shelf_book = models.ShelfBook.objects.create(
|
|
||||||
book=item.book, shelf=desired_shelf, added_by=user)
|
|
||||||
broadcast(user, shelf_book.to_add_activity(user), privacy=privacy)
|
|
||||||
|
|
||||||
for read in item.reads:
|
|
||||||
# check for an existing readthrough with the same dates
|
|
||||||
if models.ReadThrough.objects.filter(
|
|
||||||
user=user, book=item.book,
|
|
||||||
start_date=read.start_date,
|
|
||||||
finish_date=read.finish_date
|
|
||||||
).exists():
|
|
||||||
continue
|
|
||||||
read.book = item.book
|
|
||||||
read.user = user
|
|
||||||
read.save()
|
|
||||||
|
|
||||||
if include_reviews and (item.rating or item.review):
|
|
||||||
review_title = 'Review of {!r} on Goodreads'.format(
|
|
||||||
item.book.title,
|
|
||||||
) if item.review else ''
|
|
||||||
|
|
||||||
# we don't know the publication date of the review,
|
|
||||||
# but "now" is a bad guess
|
|
||||||
published_date_guess = item.date_read or item.date_added
|
|
||||||
review = models.Review.objects.create(
|
|
||||||
user=user,
|
|
||||||
book=item.book,
|
|
||||||
name=review_title,
|
|
||||||
content=item.review,
|
|
||||||
rating=item.rating,
|
|
||||||
published_date=published_date_guess,
|
|
||||||
privacy=privacy,
|
|
||||||
)
|
|
||||||
# we don't need to send out pure activities because non-bookwyrm
|
|
||||||
# instances don't need this data
|
|
||||||
broadcast(user, review.to_create_activity(user), privacy=privacy)
|
|
|
@ -3,7 +3,7 @@ from django.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, re_path
|
from django.urls import path, re_path
|
||||||
|
|
||||||
from bookwyrm import incoming, outgoing, settings, views, wellknown
|
from bookwyrm import incoming, settings, views, wellknown
|
||||||
from bookwyrm.utils import regex
|
from bookwyrm.utils import regex
|
||||||
|
|
||||||
user_path = r'^user/(?P<username>%s)' % regex.username
|
user_path = r'^user/(?P<username>%s)' % regex.username
|
||||||
|
@ -30,7 +30,7 @@ urlpatterns = [
|
||||||
# federation endpoints
|
# federation endpoints
|
||||||
re_path(r'^inbox/?$', incoming.shared_inbox),
|
re_path(r'^inbox/?$', incoming.shared_inbox),
|
||||||
re_path(r'%s/inbox/?$' % local_user_path, incoming.inbox),
|
re_path(r'%s/inbox/?$' % local_user_path, incoming.inbox),
|
||||||
re_path(r'%s/outbox/?$' % local_user_path, outgoing.outbox),
|
re_path(r'%s/outbox/?$' % local_user_path, views.Outbox.as_view()),
|
||||||
|
|
||||||
# .well-known endpoints
|
# .well-known endpoints
|
||||||
re_path(r'^.well-known/webfinger/?$', wellknown.webfinger),
|
re_path(r'^.well-known/webfinger/?$', wellknown.webfinger),
|
||||||
|
|
|
@ -6,12 +6,13 @@ from .books import upload_cover, add_description, switch_edition, resolve_book
|
||||||
from .direct_message import DirectMessage
|
from .direct_message import DirectMessage
|
||||||
from .error import not_found_page, server_error_page
|
from .error import not_found_page, server_error_page
|
||||||
from .follow import follow, unfollow
|
from .follow import follow, unfollow
|
||||||
from .follow import accept_follow_request, delete_follow_request
|
from .follow import accept_follow_request, delete_follow_request, handle_accept
|
||||||
from .import_data import Import, ImportStatus
|
from .import_data import Import, ImportStatus
|
||||||
from .interaction import Favorite, Unfavorite, Boost, Unboost
|
from .interaction import Favorite, Unfavorite, Boost, Unboost
|
||||||
from .invite import ManageInvites, Invite
|
from .invite import ManageInvites, Invite
|
||||||
from .landing import About, Home, Feed, Discover
|
from .landing import About, Home, Feed, Discover
|
||||||
from .notifications import Notifications
|
from .notifications import Notifications
|
||||||
|
from .outbox import Outbox
|
||||||
from .reading import edit_readthrough, create_readthrough, delete_readthrough
|
from .reading import edit_readthrough, create_readthrough, delete_readthrough
|
||||||
from .reading import start_reading, finish_reading
|
from .reading import start_reading, finish_reading
|
||||||
from .password import PasswordResetRequest, PasswordReset, ChangePassword
|
from .password import PasswordResetRequest, PasswordReset, ChangePassword
|
||||||
|
|
113
bookwyrm/views/follow.py
Normal file
113
bookwyrm/views/follow.py
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
''' views for actions you can take in the application '''
|
||||||
|
from django.db import transaction
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpResponseBadRequest
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
|
from bookwyrm import models
|
||||||
|
from bookwyrm.broadcast import broadcast
|
||||||
|
from .helpers import get_user_from_username
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def follow(request):
|
||||||
|
''' follow another user, here or abroad '''
|
||||||
|
username = request.POST['user']
|
||||||
|
try:
|
||||||
|
to_follow = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
relationship, _ = models.UserFollowRequest.objects.get_or_create(
|
||||||
|
user_subject=request.user,
|
||||||
|
user_object=to_follow,
|
||||||
|
)
|
||||||
|
activity = relationship.to_activity()
|
||||||
|
broadcast(
|
||||||
|
request.user, activity, privacy='direct', direct_recipients=[to_follow])
|
||||||
|
return redirect(to_follow.local_path)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def unfollow(request):
|
||||||
|
''' unfollow a user '''
|
||||||
|
username = request.POST['user']
|
||||||
|
try:
|
||||||
|
to_unfollow = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
relationship = models.UserFollows.objects.get(
|
||||||
|
user_subject=request.user,
|
||||||
|
user_object=to_unfollow
|
||||||
|
)
|
||||||
|
activity = relationship.to_undo_activity(request.user)
|
||||||
|
broadcast(
|
||||||
|
request.user, activity,
|
||||||
|
privacy='direct', direct_recipients=[to_unfollow])
|
||||||
|
|
||||||
|
to_unfollow.followers.remove(request.user)
|
||||||
|
return redirect(to_unfollow.local_path)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def accept_follow_request(request):
|
||||||
|
''' a user accepts a follow request '''
|
||||||
|
username = request.POST['user']
|
||||||
|
try:
|
||||||
|
requester = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
follow_request = models.UserFollowRequest.objects.get(
|
||||||
|
user_subject=requester,
|
||||||
|
user_object=request.user
|
||||||
|
)
|
||||||
|
except models.UserFollowRequest.DoesNotExist:
|
||||||
|
# Request already dealt with.
|
||||||
|
return redirect(request.user.local_path)
|
||||||
|
handle_accept(follow_request)
|
||||||
|
|
||||||
|
return redirect(request.user.local_path)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_accept(follow_request):
|
||||||
|
''' send an acceptance message to a follow request '''
|
||||||
|
user = follow_request.user_subject
|
||||||
|
to_follow = follow_request.user_object
|
||||||
|
with transaction.atomic():
|
||||||
|
relationship = models.UserFollows.from_request(follow_request)
|
||||||
|
follow_request.delete()
|
||||||
|
relationship.save()
|
||||||
|
|
||||||
|
activity = relationship.to_accept_activity()
|
||||||
|
broadcast(to_follow, activity, privacy='direct', direct_recipients=[user])
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def delete_follow_request(request):
|
||||||
|
''' a user rejects a follow request '''
|
||||||
|
username = request.POST['user']
|
||||||
|
try:
|
||||||
|
requester = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
follow_request = models.UserFollowRequest.objects.get(
|
||||||
|
user_subject=requester,
|
||||||
|
user_object=request.user
|
||||||
|
)
|
||||||
|
except models.UserFollowRequest.DoesNotExist:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
activity = follow_request.to_reject_activity()
|
||||||
|
follow_request.delete()
|
||||||
|
broadcast(
|
||||||
|
request.user, activity, privacy='direct', direct_recipients=[requester])
|
||||||
|
return redirect('/user/%s' % request.user.localname)
|
22
bookwyrm/views/outbox.py
Normal file
22
bookwyrm/views/outbox.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
''' the good stuff! the books! '''
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
from bookwyrm import activitypub, models
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable= no-self-use
|
||||||
|
class Outbox(View):
|
||||||
|
''' outbox '''
|
||||||
|
def get(self, request, username):
|
||||||
|
''' outbox for the requested user '''
|
||||||
|
user = get_object_or_404(models.User, localname=username)
|
||||||
|
filter_type = request.GET.get('type')
|
||||||
|
if filter_type not in models.status_models:
|
||||||
|
filter_type = None
|
||||||
|
|
||||||
|
return JsonResponse(
|
||||||
|
user.to_outbox(**request.GET, filter_type=filter_type),
|
||||||
|
encoder=activitypub.ActivityEncoder
|
||||||
|
)
|
Loading…
Reference in a new issue