Merge branch 'main' into list-embed
This commit is contained in:
@ -29,6 +29,7 @@ from .preferences.block import Block, unblock
|
||||
|
||||
# books
|
||||
from .books.books import Book, upload_cover, add_description, resolve_book
|
||||
from .books.books import update_book_from_remote
|
||||
from .books.edit_book import EditBook, ConfirmEditBook
|
||||
from .books.editions import Editions, switch_edition
|
||||
|
||||
@ -54,11 +55,18 @@ from .imports.manually_review import (
|
||||
)
|
||||
|
||||
# misc views
|
||||
from .author import Author, EditAuthor
|
||||
from .author import Author, EditAuthor, update_author_from_remote
|
||||
from .directory import Directory
|
||||
from .discover import Discover
|
||||
from .feed import DirectMessage, Feed, Replies, Status
|
||||
from .follow import follow, unfollow
|
||||
from .follow import (
|
||||
follow,
|
||||
unfollow,
|
||||
ostatus_follow_request,
|
||||
ostatus_follow_success,
|
||||
remote_follow,
|
||||
remote_follow_page,
|
||||
)
|
||||
from .follow import accept_follow_request, delete_follow_request
|
||||
from .get_started import GetStartedBooks, GetStartedProfile, GetStartedUsers
|
||||
from .goal import Goal, hide_goal
|
||||
|
@ -6,9 +6,11 @@ from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.activitypub import ActivitypubResponse
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
from bookwyrm.views.helpers import is_api_request
|
||||
|
||||
@ -73,3 +75,19 @@ class EditAuthor(View):
|
||||
author = form.save()
|
||||
|
||||
return redirect(f"/author/{author.id}")
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
@permission_required("bookwyrm.edit_book", raise_exception=True)
|
||||
# pylint: disable=unused-argument
|
||||
def update_author_from_remote(request, author_id, connector_identifier):
|
||||
"""load the remote data for this author"""
|
||||
connector = connector_manager.load_connector(
|
||||
get_object_or_404(models.Connector, identifier=connector_identifier)
|
||||
)
|
||||
author = get_object_or_404(models.Author, id=author_id)
|
||||
|
||||
connector.update_author_from_remote(author)
|
||||
|
||||
return redirect("author", author.id)
|
||||
|
@ -178,3 +178,19 @@ def resolve_book(request):
|
||||
book = connector.get_or_create_book(remote_id)
|
||||
|
||||
return redirect("book", book.id)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
@permission_required("bookwyrm.edit_book", raise_exception=True)
|
||||
# pylint: disable=unused-argument
|
||||
def update_book_from_remote(request, book_id, connector_identifier):
|
||||
"""load the remote data for this book"""
|
||||
connector = connector_manager.load_connector(
|
||||
get_object_or_404(models.Connector, identifier=connector_identifier)
|
||||
)
|
||||
book = get_object_or_404(models.Book.objects.select_subclasses(), id=book_id)
|
||||
|
||||
connector.update_book_from_remote(book)
|
||||
|
||||
return redirect("book", book.id)
|
||||
|
@ -1,11 +1,19 @@
|
||||
""" views for actions you can take in the application """
|
||||
import urllib.parse
|
||||
import re
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db import IntegrityError
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from bookwyrm import models
|
||||
from .helpers import get_user_from_username
|
||||
from .helpers import (
|
||||
get_user_from_username,
|
||||
handle_remote_webfinger,
|
||||
subscribe_remote_webfinger,
|
||||
WebFingerError,
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@ -23,6 +31,9 @@ def follow(request):
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
if request.GET.get("next"):
|
||||
return redirect(request.GET.get("next", "/"))
|
||||
|
||||
return redirect(to_follow.local_path)
|
||||
|
||||
|
||||
@ -84,3 +95,91 @@ def delete_follow_request(request):
|
||||
|
||||
follow_request.delete()
|
||||
return redirect(f"/user/{request.user.localname}")
|
||||
|
||||
|
||||
def ostatus_follow_request(request):
|
||||
"""prepare an outgoing remote follow request"""
|
||||
uri = urllib.parse.unquote(request.GET.get("acct"))
|
||||
username_parts = re.search(
|
||||
r"(?:^http(?:s?):\/\/)([\w\-\.]*)(?:.)*(?:(?:\/)([\w]*))", uri
|
||||
)
|
||||
account = f"{username_parts[2]}@{username_parts[1]}"
|
||||
user = handle_remote_webfinger(account)
|
||||
error = None
|
||||
|
||||
if user is None or user == "":
|
||||
error = "ostatus_subscribe"
|
||||
|
||||
# don't do these checks for AnonymousUser before they sign in
|
||||
if request.user.is_authenticated:
|
||||
|
||||
# you have blocked them so you probably don't want to follow
|
||||
if hasattr(request.user, "blocks") and user in request.user.blocks.all():
|
||||
error = "is_blocked"
|
||||
# they have blocked you
|
||||
if hasattr(user, "blocks") and request.user in user.blocks.all():
|
||||
error = "has_blocked"
|
||||
# you're already following them
|
||||
if hasattr(user, "followers") and request.user in user.followers.all():
|
||||
error = "already_following"
|
||||
# you're not following yet but you already asked
|
||||
if (
|
||||
hasattr(user, "follower_requests")
|
||||
and request.user in user.follower_requests.all()
|
||||
):
|
||||
error = "already_requested"
|
||||
|
||||
data = {"account": account, "user": user, "error": error}
|
||||
|
||||
return TemplateResponse(request, "ostatus/subscribe.html", data)
|
||||
|
||||
|
||||
@login_required
|
||||
def ostatus_follow_success(request):
|
||||
"""display success message for remote follow"""
|
||||
user = get_user_from_username(request.user, request.GET.get("following"))
|
||||
data = {"account": user.name, "user": user, "error": None}
|
||||
return TemplateResponse(request, "ostatus/success.html", data)
|
||||
|
||||
|
||||
def remote_follow_page(request):
|
||||
"""display remote follow page"""
|
||||
user = get_user_from_username(request.user, request.GET.get("user"))
|
||||
data = {"user": user}
|
||||
return TemplateResponse(request, "ostatus/remote_follow.html", data)
|
||||
|
||||
|
||||
@require_POST
|
||||
def remote_follow(request):
|
||||
"""direct user to follow from remote account using ostatus subscribe protocol"""
|
||||
remote_user = request.POST.get("remote_user")
|
||||
try:
|
||||
if remote_user[0] == "@":
|
||||
remote_user = remote_user[1:]
|
||||
remote_domain = remote_user.split("@")[1]
|
||||
except (TypeError, IndexError):
|
||||
remote_domain = None
|
||||
|
||||
wf_response = subscribe_remote_webfinger(remote_user)
|
||||
user = get_object_or_404(models.User, id=request.POST.get("user"))
|
||||
|
||||
if wf_response is None:
|
||||
data = {
|
||||
"account": remote_user,
|
||||
"user": user,
|
||||
"error": "not_supported",
|
||||
"remote_domain": remote_domain,
|
||||
}
|
||||
return TemplateResponse(request, "ostatus/subscribe.html", data)
|
||||
|
||||
if isinstance(wf_response, WebFingerError):
|
||||
data = {
|
||||
"account": remote_user,
|
||||
"user": user,
|
||||
"error": str(wf_response),
|
||||
"remote_domain": remote_domain,
|
||||
}
|
||||
return TemplateResponse(request, "ostatus/subscribe.html", data)
|
||||
|
||||
url = wf_response.replace("{uri}", urllib.parse.quote(user.remote_id))
|
||||
return redirect(url)
|
||||
|
@ -16,6 +16,13 @@ from bookwyrm.status import create_generated_note
|
||||
from bookwyrm.utils import regex
|
||||
|
||||
|
||||
# pylint: disable=unnecessary-pass
|
||||
class WebFingerError(Exception):
|
||||
"""empty error class for problems finding user information with webfinger"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def get_user_from_username(viewer, username):
|
||||
"""helper function to resolve a localname or a username to a user"""
|
||||
if viewer.is_authenticated and viewer.localname == username:
|
||||
@ -57,10 +64,8 @@ def handle_remote_webfinger(query):
|
||||
# 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:
|
||||
@ -86,6 +91,35 @@ def handle_remote_webfinger(query):
|
||||
return user
|
||||
|
||||
|
||||
def subscribe_remote_webfinger(query):
|
||||
"""get subscribe template from other servers"""
|
||||
template = None
|
||||
# usernames could be @user@domain or user@domain
|
||||
if not query:
|
||||
return WebFingerError("invalid_username")
|
||||
|
||||
if query[0] == "@":
|
||||
query = query[1:]
|
||||
|
||||
try:
|
||||
domain = query.split("@")[1]
|
||||
except IndexError:
|
||||
return WebFingerError("invalid_username")
|
||||
|
||||
url = f"https://{domain}/.well-known/webfinger?resource=acct:{query}"
|
||||
|
||||
try:
|
||||
data = get_data(url)
|
||||
except (ConnectorException, HTTPError):
|
||||
return WebFingerError("user_not_found")
|
||||
|
||||
for link in data.get("links"):
|
||||
if link.get("rel") == "http://ostatus.org/schema/1.0/subscribe":
|
||||
template = link["template"]
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def get_edition(book_id):
|
||||
"""look up a book in the db and return an edition"""
|
||||
book = models.Book.objects.select_subclasses().get(id=book_id)
|
||||
|
@ -30,7 +30,11 @@ def webfinger(request):
|
||||
"rel": "self",
|
||||
"type": "application/activity+json",
|
||||
"href": user.remote_id,
|
||||
}
|
||||
},
|
||||
{
|
||||
"rel": "http://ostatus.org/schema/1.0/subscribe",
|
||||
"template": f"https://{DOMAIN}/ostatus_subscribe?acct={{uri}}",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
Reference in New Issue
Block a user