bookwyrm-mastodon/bookwyrm/views/helpers.py

181 lines
5.8 KiB
Python
Raw Normal View History

2021-03-08 11:49:10 -05:00
""" helper functions used in various views """
2021-01-12 17:43:59 -05:00
import re
from requests import HTTPError
2021-02-24 14:59:21 -05:00
from django.core.exceptions import FieldError
from django.db.models import Q
from django.http import Http404
2021-01-12 17:43:59 -05:00
from bookwyrm import activitypub, models
from bookwyrm.connectors import ConnectorException, get_data
2021-01-13 14:45:08 -05:00
from bookwyrm.status import create_generated_note
2021-01-12 17:43:59 -05:00
from bookwyrm.utils import regex
2021-01-12 13:44:17 -05:00
2021-02-23 15:41:37 -05:00
def get_user_from_username(viewer, username):
2021-04-26 12:15:42 -04:00
"""helper function to resolve a localname or a username to a user"""
2021-05-23 00:33:56 -04:00
if viewer.is_authenticated and viewer.localname == username:
# that's yourself, fool
return viewer
# raises 404 if the user isn't found
2021-01-12 15:05:30 -05:00
try:
2021-02-23 16:05:43 -05:00
return models.User.viewer_aware_objects(viewer).get(localname=username)
2021-01-12 15:05:30 -05:00
except models.User.DoesNotExist:
pass
# if the localname didn't match, try the username
try:
2021-02-23 15:41:37 -05:00
return models.User.viewer_aware_objects(viewer).get(username=username)
except models.User.DoesNotExist:
raise Http404()
2021-01-12 15:05:30 -05:00
def is_api_request(request):
2021-04-26 12:15:42 -04:00
"""check whether a request is asking for html or data"""
return "json" in request.headers.get("Accept", "") or request.path[-5:] == ".json"
2021-01-12 15:05:30 -05:00
2021-02-23 14:34:15 -05:00
def is_bookwyrm_request(request):
2021-04-26 12:15:42 -04:00
"""check if the request is coming from another bookwyrm instance"""
2021-03-08 11:49:10 -05:00
user_agent = request.headers.get("User-Agent")
2021-06-18 17:12:56 -04:00
if user_agent is None or re.search(regex.BOOKWYRM_USER_AGENT, user_agent) is None:
2021-01-12 16:47:00 -05:00
return False
return True
2021-02-24 14:35:19 -05:00
def privacy_filter(viewer, queryset, privacy_levels=None, following_only=False):
2021-04-26 12:15:42 -04:00
"""filter objects that have "user" and "privacy" fields"""
2021-03-08 11:49:10 -05:00
privacy_levels = privacy_levels or ["public", "unlisted", "followers", "direct"]
2021-03-22 22:17:46 -04:00
# if there'd a deleted field, exclude deleted items
try:
queryset = queryset.filter(deleted=False)
except FieldError:
pass
2021-02-24 14:35:19 -05:00
2021-01-24 19:13:26 -05:00
# exclude blocks from both directions
2021-01-31 11:41:11 -05:00
if not viewer.is_anonymous:
blocked = models.User.objects.filter(id__in=viewer.blocks.all()).all()
2021-03-08 11:49:10 -05:00
queryset = queryset.exclude(Q(user__in=blocked) | Q(user__blocks=viewer))
2021-01-24 19:13:26 -05:00
2021-01-12 13:44:17 -05:00
# you can't see followers only or direct messages if you're not logged in
2021-01-31 11:41:11 -05:00
if viewer.is_anonymous:
2021-03-08 11:49:10 -05:00
privacy_levels = [p for p in privacy_levels if not p in ["followers", "direct"]]
2021-01-12 13:44:17 -05:00
# filter to only privided privacy levels
2021-01-31 11:41:11 -05:00
queryset = queryset.filter(privacy__in=privacy_levels)
2021-01-12 13:44:17 -05:00
# only include statuses the user follows
if following_only:
queryset = queryset.exclude(
2021-03-08 11:49:10 -05:00
~Q( # remove everythign except
Q(user__in=viewer.following.all())
| Q(user=viewer) # user following
| Q(mention_users=viewer) # is self # mentions user
2021-01-12 13:44:17 -05:00
),
)
# exclude followers-only statuses the user doesn't follow
2021-03-08 11:49:10 -05:00
elif "followers" in privacy_levels:
2021-01-12 13:44:17 -05:00
queryset = queryset.exclude(
2021-03-08 11:49:10 -05:00
~Q( # user isn't following and it isn't their own status
2021-01-31 11:41:11 -05:00
Q(user__in=viewer.following.all()) | Q(user=viewer)
2021-01-12 13:44:17 -05:00
),
2021-03-08 11:49:10 -05:00
privacy="followers", # and the status is followers only
2021-01-12 13:44:17 -05:00
)
# exclude direct messages not intended for the user
2021-03-08 11:49:10 -05:00
if "direct" in privacy_levels:
2021-02-24 14:59:21 -05:00
try:
queryset = queryset.exclude(
2021-03-08 11:49:10 -05:00
~Q(Q(user=viewer) | Q(mention_users=viewer)), privacy="direct"
2021-02-24 14:59:21 -05:00
)
except FieldError:
2021-03-08 11:49:10 -05:00
queryset = queryset.exclude(~Q(user=viewer), privacy="direct")
2021-02-24 14:59:21 -05:00
2021-01-31 11:41:11 -05:00
return queryset
2021-01-12 16:47:00 -05:00
def handle_remote_webfinger(query):
2021-04-26 12:15:42 -04:00
"""webfingerin' other servers"""
2021-01-12 16:47:00 -05:00
user = None
# usernames could be @user@domain or user@domain
if not query:
return None
2021-03-08 11:49:10 -05:00
if query[0] == "@":
2021-01-12 16:47:00 -05:00
query = query[1:]
try:
2021-03-08 11:49:10 -05:00
domain = query.split("@")[1]
2021-01-12 16:47:00 -05:00
except IndexError:
return None
try:
2021-04-08 12:59:21 -04:00
user = models.User.objects.get(username__iexact=query)
2021-01-12 16:47:00 -05:00
except models.User.DoesNotExist:
2021-03-08 11:49:10 -05:00
url = "https://%s/.well-known/webfinger?resource=acct:%s" % (domain, query)
2021-01-12 16:47:00 -05:00
try:
data = get_data(url)
except (ConnectorException, HTTPError):
return None
2021-03-08 11:49:10 -05:00
for link in data.get("links"):
if link.get("rel") == "self":
2021-01-12 16:47:00 -05:00
try:
user = activitypub.resolve_remote_id(
2021-03-08 11:49:10 -05:00
link["href"], model=models.User
2021-01-12 16:47:00 -05:00
)
except (KeyError, activitypub.ActivitySerializerError):
2021-01-12 16:47:00 -05:00
return None
return user
def get_edition(book_id):
2021-04-26 12:15:42 -04:00
"""look up a book in the db and return an edition"""
book = models.Book.objects.select_subclasses().get(id=book_id)
if isinstance(book, models.Work):
book = book.default_edition
return book
2021-01-13 14:45:08 -05:00
def handle_reading_status(user, shelf, book, privacy):
2021-04-26 12:15:42 -04:00
"""post about a user reading a book"""
2021-01-13 14:45:08 -05:00
# tell the world about this cool thing that happened
try:
message = {
2021-03-08 11:49:10 -05:00
"to-read": "wants to read",
"reading": "started reading",
"read": "finished reading",
2021-01-13 14:45:08 -05:00
}[shelf.identifier]
except KeyError:
# it's a non-standard shelf, don't worry about it
return
2021-03-08 11:49:10 -05:00
status = create_generated_note(user, message, mention_books=[book], privacy=privacy)
2021-01-13 14:45:08 -05:00
status.save()
2021-01-26 11:31:55 -05:00
def is_blocked(viewer, user):
2021-04-26 12:15:42 -04:00
"""is this viewer blocked by the user?"""
2021-01-26 11:31:55 -05:00
if viewer.is_authenticated and viewer in user.blocks.all():
return True
return False
2021-03-20 22:14:41 -04:00
2021-08-07 14:15:02 -04:00
def get_landing_books():
"""list of books for the landing page"""
return list(
set(
models.Edition.objects.filter(
review__published_date__isnull=False,
review__deleted=False,
review__user__local=True,
review__privacy__in=["public", "unlisted"],
)
.exclude(cover__exact="")
.distinct()
.order_by("-review__published_date")[:6]
2021-03-20 22:14:41 -04:00
)
2021-05-22 12:55:38 -04:00
)