forked from mirrors/bookwyrm
parent
8f766c7fb4
commit
4765f965cf
3 changed files with 128 additions and 114 deletions
|
@ -122,96 +122,6 @@ def inbox(request, username):
|
||||||
return shared_inbox(request)
|
return shared_inbox(request)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
def get_actor(request, username):
|
|
||||||
''' return an activitypub actor object '''
|
|
||||||
if request.method != 'GET':
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = models.User.objects.get(localname=username)
|
|
||||||
except models.User.DoesNotExist:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
return JsonResponse(activitypub.get_actor(user))
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
def get_status(request, username, status_id):
|
|
||||||
''' return activity json for a specific status '''
|
|
||||||
if request.method != 'GET':
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = models.User.objects.get(localname=username)
|
|
||||||
status = models.Status.objects.get(id=status_id)
|
|
||||||
except ValueError:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
if user != status.user:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
return JsonResponse(activitypub.get_status(status))
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
def get_replies(request, username, status_id):
|
|
||||||
''' ordered collection of replies to a status '''
|
|
||||||
# TODO: this isn't a full implmentation
|
|
||||||
if request.method != 'GET':
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
status = models.Status.objects.get(id=status_id)
|
|
||||||
if status.user.localname != username:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
|
|
||||||
replies = models.Status.objects.filter(
|
|
||||||
reply_parent=status,
|
|
||||||
).select_subclasses()
|
|
||||||
|
|
||||||
if request.GET.get('only_other_accounts'):
|
|
||||||
replies = replies.filter(
|
|
||||||
~Q(user=status.user)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
replies = replies.filter(user=status.user)
|
|
||||||
|
|
||||||
if request.GET.get('page'):
|
|
||||||
min_id = request.GET.get('min_id')
|
|
||||||
if min_id:
|
|
||||||
replies = replies.filter(id__gt=min_id)
|
|
||||||
max_id = request.GET.get('max_id')
|
|
||||||
if max_id:
|
|
||||||
replies = replies.filter(id__lte=max_id)
|
|
||||||
activity = activitypub.get_replies_page(status, replies)
|
|
||||||
return JsonResponse(activity)
|
|
||||||
|
|
||||||
return JsonResponse(activitypub.get_replies(status, replies))
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
def get_followers(request, username):
|
|
||||||
''' return a list of followers for an actor '''
|
|
||||||
if request.method != 'GET':
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
user = models.User.objects.get(localname=username)
|
|
||||||
followers = user.followers
|
|
||||||
page = request.GET.get('page')
|
|
||||||
return JsonResponse(activitypub.get_followers(user, page, followers))
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
def get_following(request, username):
|
|
||||||
''' return a list of following for an actor '''
|
|
||||||
if request.method != 'GET':
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
user = models.User.objects.get(localname=username)
|
|
||||||
following = user.following
|
|
||||||
page = request.GET.get('page')
|
|
||||||
return JsonResponse(activitypub.get_following(user, page, following))
|
|
||||||
|
|
||||||
|
|
||||||
def handle_incoming_follow(activity):
|
def handle_incoming_follow(activity):
|
||||||
''' someone wants to follow a local user '''
|
''' someone wants to follow a local user '''
|
||||||
# figure out who they want to follow
|
# figure out who they want to follow
|
||||||
|
@ -263,7 +173,10 @@ def handle_incoming_follow_accept(activity):
|
||||||
accepter = get_or_create_remote_user(activity['actor'])
|
accepter = get_or_create_remote_user(activity['actor'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
request = models.UserFollowRequest.objects.get(user_subject=requester, user_object=accepter)
|
request = models.UserFollowRequest.objects.get(
|
||||||
|
user_subject=requester,
|
||||||
|
user_object=accepter
|
||||||
|
)
|
||||||
request.delete()
|
request.delete()
|
||||||
except models.UserFollowRequest.DoesNotExist:
|
except models.UserFollowRequest.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -6,8 +6,8 @@ from django.urls import path, re_path
|
||||||
from fedireads import incoming, outgoing, views, settings, wellknown
|
from fedireads import incoming, outgoing, views, settings, wellknown
|
||||||
from fedireads import view_actions as actions
|
from fedireads import view_actions as actions
|
||||||
|
|
||||||
username_regex = r'(?P<username>[\w@\.-]+)'
|
username_regex = r'(?P<username>[\w@\-_]+)'
|
||||||
localname_regex = r'(?P<username>[\w\.-]+)'
|
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_path = r'%s/(status|review)/(?P<status_id>\d+)' % local_user_path
|
status_path = r'%s/(status|review)/(?P<status_id>\d+)' % local_user_path
|
||||||
|
@ -17,14 +17,8 @@ urlpatterns = [
|
||||||
|
|
||||||
# federation endpoints
|
# federation endpoints
|
||||||
re_path(r'^inbox/?$', incoming.shared_inbox),
|
re_path(r'^inbox/?$', incoming.shared_inbox),
|
||||||
re_path(r'%s.json$' % local_user_path, incoming.get_actor),
|
|
||||||
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, outgoing.outbox),
|
||||||
re_path(r'%s/followers/?$' % local_user_path, incoming.get_followers),
|
|
||||||
re_path(r'%s/following/?$' % local_user_path, incoming.get_following),
|
|
||||||
re_path(r'%s.json$' % status_path, incoming.get_status),
|
|
||||||
re_path(r'%s/activity/?$' % status_path, incoming.get_status),
|
|
||||||
re_path(r'%s/replies/?$' % status_path, incoming.get_replies),
|
|
||||||
|
|
||||||
# .well-known endpoints
|
# .well-known endpoints
|
||||||
re_path(r'^.well-known/webfinger/?$', wellknown.webfinger),
|
re_path(r'^.well-known/webfinger/?$', wellknown.webfinger),
|
||||||
|
@ -34,15 +28,32 @@ urlpatterns = [
|
||||||
# TODO: re_path(r'^.well-known/host-meta/?$', incoming.host_meta),
|
# TODO: re_path(r'^.well-known/host-meta/?$', incoming.host_meta),
|
||||||
|
|
||||||
# ui views
|
# ui views
|
||||||
path('', views.home),
|
|
||||||
re_path(r'^(?P<tab>home|local|federated)/?$', views.home_tab),
|
|
||||||
re_path(r'^register/?$', views.register),
|
re_path(r'^register/?$', views.register),
|
||||||
re_path(r'^login/?$', views.user_login),
|
re_path(r'^login/?$', views.user_login),
|
||||||
re_path(r'^logout/?$', views.user_logout),
|
re_path(r'^logout/?$', views.user_logout),
|
||||||
|
|
||||||
|
# should return a ui view or activitypub json blob as requested
|
||||||
|
path('', views.home),
|
||||||
|
re_path(r'^(?P<tab>home|local|federated)/?$', views.home_tab),
|
||||||
re_path(r'^notifications/?', views.notifications_page),
|
re_path(r'^notifications/?', views.notifications_page),
|
||||||
|
|
||||||
|
# users
|
||||||
re_path(r'%s/?$' % user_path, views.user_page),
|
re_path(r'%s/?$' % user_path, views.user_page),
|
||||||
|
re_path(r'%s\.json$' % local_user_path, views.user_page),
|
||||||
re_path(r'edit_profile_page/?$', views.edit_profile_page),
|
re_path(r'edit_profile_page/?$', views.edit_profile_page),
|
||||||
|
re_path(r'%s/followers/?$' % local_user_path, views.followers_page),
|
||||||
|
re_path(r'%s/followers.json$' % local_user_path, views.followers_page),
|
||||||
|
re_path(r'%s/following/?$' % local_user_path, views.following_page),
|
||||||
|
re_path(r'%s/following.json$' % local_user_path, views.following_page),
|
||||||
|
|
||||||
|
# statuses
|
||||||
re_path(r'%s/?$' % status_path, views.status_page),
|
re_path(r'%s/?$' % status_path, views.status_page),
|
||||||
|
re_path(r'%s.json$' % status_path, views.status_page),
|
||||||
|
re_path(r'%s/activity/?$' % status_path, views.status_page),
|
||||||
|
re_path(r'%s/replies/?$' % status_path, views.replies_page),
|
||||||
|
re_path(r'%s/replies\.json$' % status_path, views.replies_page),
|
||||||
|
|
||||||
|
# books
|
||||||
re_path(r'^book/(?P<book_identifier>\w+)/?$', views.book_page),
|
re_path(r'^book/(?P<book_identifier>\w+)/?$', views.book_page),
|
||||||
re_path(r'^book/(?P<book_identifier>\w+)/(?P<tab>friends|local|federated)?$', views.book_page),
|
re_path(r'^book/(?P<book_identifier>\w+)/(?P<tab>friends|local|federated)?$', views.book_page),
|
||||||
re_path(r'^author/(?P<author_identifier>\w+)/?$', views.author_page),
|
re_path(r'^author/(?P<author_identifier>\w+)/?$', views.author_page),
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import Avg, Q
|
from django.db.models import Avg, Q
|
||||||
from django.http import HttpResponseNotFound
|
from django.http import HttpResponseBadRequest, HttpResponseNotFound, \
|
||||||
|
JsonResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from fedireads import forms, models, books_manager, incoming
|
from fedireads import activitypub
|
||||||
|
from fedireads import forms, models, books_manager
|
||||||
from fedireads.settings import DOMAIN
|
from fedireads.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,6 +22,14 @@ def get_user_from_username(username):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def is_api_request(request):
|
||||||
|
''' check whether a request is asking for html or data '''
|
||||||
|
# TODO: this should probably be the full content type? maybe?
|
||||||
|
return 'json' in request.headers.get('Accept') or \
|
||||||
|
request.path[-5:] == '.json'
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def home(request):
|
def home(request):
|
||||||
''' this is the same as the feed on the home tab '''
|
''' this is the same as the feed on the home tab '''
|
||||||
|
@ -153,20 +164,19 @@ def notifications_page(request):
|
||||||
return TemplateResponse(request, 'notifications.html', data)
|
return TemplateResponse(request, 'notifications.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
def user_page(request, username):
|
def user_page(request, username):
|
||||||
''' profile page for a user '''
|
''' profile page for a user '''
|
||||||
content = request.headers.get('Accept')
|
|
||||||
# TODO: this should probably be the full content type? maybe?
|
|
||||||
if 'json' in content:
|
|
||||||
# we have a json request
|
|
||||||
return incoming.get_actor(request, username)
|
|
||||||
|
|
||||||
# otherwise we're at a UI view
|
|
||||||
try:
|
try:
|
||||||
user = get_user_from_username(username)
|
user = get_user_from_username(username)
|
||||||
except models.User.DoesNotExist:
|
except models.User.DoesNotExist:
|
||||||
return HttpResponseNotFound()
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
# we have a json request
|
||||||
|
return JsonResponse(activitypub.get_actor(user))
|
||||||
|
# otherwise we're at a UI view
|
||||||
|
|
||||||
# TODO: change display with privacy and authentication considerations
|
# TODO: change display with privacy and authentication considerations
|
||||||
shelves = models.Shelf.objects.filter(user=user)
|
shelves = models.Shelf.objects.filter(user=user)
|
||||||
ratings = {r.book.id: r.rating for r in \
|
ratings = {r.book.id: r.rating for r in \
|
||||||
|
@ -181,12 +191,52 @@ def user_page(request, username):
|
||||||
return TemplateResponse(request, 'user.html', data)
|
return TemplateResponse(request, 'user.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def followers_page(request, username):
|
||||||
|
''' list of followers '''
|
||||||
|
if request.method != 'GET':
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
user = models.User.objects.get(localname=username)
|
||||||
|
followers = user.followers
|
||||||
|
page = request.GET.get('page')
|
||||||
|
return JsonResponse(activitypub.get_followers(user, page, followers))
|
||||||
|
|
||||||
|
return redirect('/user/' + username)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def following_page(request, username):
|
||||||
|
''' list of followers '''
|
||||||
|
if request.method != 'GET':
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = get_user_from_username(username)
|
||||||
|
except models.User.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
user = models.User.objects.get(localname=username)
|
||||||
|
following = user.following
|
||||||
|
page = request.GET.get('page')
|
||||||
|
return JsonResponse(activitypub.get_following(user, page, following))
|
||||||
|
|
||||||
|
return redirect('/user/' + username)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
def status_page(request, username, status_id):
|
def status_page(request, username, status_id):
|
||||||
''' display a particular status (and replies, etc) '''
|
''' display a particular status (and replies, etc) '''
|
||||||
content = request.headers.get('Accept')
|
if request.method != 'GET':
|
||||||
if 'json' in content:
|
return HttpResponseBadRequest()
|
||||||
# we have a json request
|
|
||||||
return incoming.get_status(request, username, status_id)
|
|
||||||
try:
|
try:
|
||||||
user = get_user_from_username(username)
|
user = get_user_from_username(username)
|
||||||
status = models.Status.objects.select_subclasses().get(id=status_id)
|
status = models.Status.objects.select_subclasses().get(id=status_id)
|
||||||
|
@ -196,12 +246,52 @@ def status_page(request, username, status_id):
|
||||||
if user != status.user:
|
if user != status.user:
|
||||||
return HttpResponseNotFound()
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
if is_api_request(request):
|
||||||
|
return JsonResponse(activitypub.get_status(status))
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'status': status,
|
'status': status,
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, 'status.html', data)
|
return TemplateResponse(request, 'status.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def replies_page(request, username, status_id):
|
||||||
|
''' ordered collection of replies to a status '''
|
||||||
|
if request.method != 'GET':
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
if not is_api_request(request):
|
||||||
|
return status_page(request, username, status_id)
|
||||||
|
|
||||||
|
status = models.Status.objects.get(id=status_id)
|
||||||
|
if status.user.localname != username:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
replies = models.Status.objects.filter(
|
||||||
|
reply_parent=status,
|
||||||
|
).select_subclasses()
|
||||||
|
|
||||||
|
if request.GET.get('only_other_accounts'):
|
||||||
|
replies = replies.filter(
|
||||||
|
~Q(user=status.user)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
replies = replies.filter(user=status.user)
|
||||||
|
|
||||||
|
if request.GET.get('page'):
|
||||||
|
min_id = request.GET.get('min_id')
|
||||||
|
if min_id:
|
||||||
|
replies = replies.filter(id__gt=min_id)
|
||||||
|
max_id = request.GET.get('max_id')
|
||||||
|
if max_id:
|
||||||
|
replies = replies.filter(id__lte=max_id)
|
||||||
|
activity = activitypub.get_replies_page(status, replies)
|
||||||
|
return JsonResponse(activity)
|
||||||
|
|
||||||
|
return JsonResponse(activitypub.get_replies(status, replies))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def edit_profile_page(request):
|
def edit_profile_page(request):
|
||||||
''' profile page for a user '''
|
''' profile page for a user '''
|
||||||
|
|
Loading…
Reference in a new issue