Merge branch 'main' into rss-feed
This commit is contained in:
commit
bfc8856f66
57 changed files with 981 additions and 846 deletions
|
@ -83,7 +83,7 @@ class Undo(Verb):
|
|||
def action(self):
|
||||
"""find and remove the activity object"""
|
||||
if isinstance(self.object, str):
|
||||
# it may be that sometihng should be done with these, but idk what
|
||||
# it may be that something should be done with these, but idk what
|
||||
# this seems just to be coming from pleroma
|
||||
return
|
||||
|
||||
|
@ -94,7 +94,7 @@ class Undo(Verb):
|
|||
model = apps.get_model("bookwyrm.UserFollows")
|
||||
obj = self.object.to_model(model=model, save=False, allow_create=False)
|
||||
if not obj:
|
||||
# this could be a folloq request not a follow proper
|
||||
# this could be a follow request not a follow proper
|
||||
model = apps.get_model("bookwyrm.UserFollowRequest")
|
||||
obj = self.object.to_model(model=model, save=False, allow_create=False)
|
||||
else:
|
||||
|
|
|
@ -20,7 +20,7 @@ def search(query, min_confidence=0, filters=None, return_first=False):
|
|||
query = query.strip()
|
||||
|
||||
results = None
|
||||
# first, try searching unqiue identifiers
|
||||
# first, try searching unique identifiers
|
||||
# unique identifiers never have spaces, title/author usually do
|
||||
if not " " in query:
|
||||
results = search_identifiers(query, *filters, return_first=return_first)
|
||||
|
|
|
@ -33,8 +33,8 @@ class AuthorForm(CustomForm):
|
|||
),
|
||||
"born": forms.SelectDateWidget(attrs={"aria-describedby": "desc_born"}),
|
||||
"died": forms.SelectDateWidget(attrs={"aria-describedby": "desc_died"}),
|
||||
"oepnlibrary_key": forms.TextInput(
|
||||
attrs={"aria-describedby": "desc_oepnlibrary_key"}
|
||||
"openlibrary_key": forms.TextInput(
|
||||
attrs={"aria-describedby": "desc_openlibrary_key"}
|
||||
),
|
||||
"inventaire_id": forms.TextInput(
|
||||
attrs={"aria-describedby": "desc_inventaire_id"}
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<label class="label" for="id_openlibrary_key">{% trans "Openlibrary key:" %}</label>
|
||||
{{ form.openlibrary_key }}
|
||||
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.oepnlibrary_key.errors id="desc_oepnlibrary_key" %}
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.openlibrary_key.errors id="desc_openlibrary_key" %}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
{% block title %}{{ book|book_title }}{% endblock %}
|
||||
|
||||
{% block opengraph_images %}
|
||||
{% include 'snippets/opengraph_images.html' with image=book.preview_image %}
|
||||
{% block opengraph %}
|
||||
{% include 'snippets/opengraph.html' with image=book.preview_image %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
<a href="{{ user_path}}">{{ username }}</a> wants to read <a href="{{ book_path }}">{{ book_title }}</a>
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% if finished reading or status.content == '<p>finished reading</p>' %}
|
||||
{% if status.content == 'finished reading' or status.content == '<p>finished reading</p>' %}
|
||||
{% blocktrans trimmed %}
|
||||
<a href="{{ user_path}}">{{ username }}</a> finished reading <a href="{{ book_path }}">{{ book_title }}</a>
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
{% if started reading or status.content == '<p>started reading</p>' %}
|
||||
{% if status.content == 'started reading' or status.content == '<p>started reading</p>' %}
|
||||
{% blocktrans trimmed %}
|
||||
<a href="{{ user_path}}">{{ username }}</a> started reading <a href="{{ book_path }}">{{ book_title }}</a>
|
||||
{% endblocktrans %}
|
||||
|
|
|
@ -2,15 +2,13 @@
|
|||
{% load feed_page_tags %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block opengraph_images %}
|
||||
|
||||
{% firstof status.book status.mention_books.first as book %}
|
||||
{% if book %}
|
||||
{% include 'snippets/opengraph_images.html' with image=preview %}
|
||||
{% else %}
|
||||
{% include 'snippets/opengraph_images.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% block opengraph %}
|
||||
{% firstof status.book status.mention_books.first as book %}
|
||||
{% if book %}
|
||||
{% include 'snippets/opengraph.html' with image=preview %}
|
||||
{% else %}
|
||||
{% include 'snippets/opengraph.html' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@ -44,4 +42,3 @@
|
|||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -15,20 +15,9 @@
|
|||
<link rel="shortcut icon" type="image/x-icon" href="{% if site.favicon %}{% get_media_prefix %}{{ site.favicon }}{% else %}{% static "images/favicon.ico" %}{% endif %}">
|
||||
<link rel="apple-touch-icon" href="{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}">
|
||||
|
||||
{% if preview_images_enabled is True %}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
{% else %}
|
||||
<meta name="twitter:card" content="summary">
|
||||
{% endif %}
|
||||
<meta name="twitter:title" content="{% if title %}{{ title }} - {% endif %}{{ site.name }}">
|
||||
<meta name="og:title" content="{% if title %}{{ title }} - {% endif %}{{ site.name }}">
|
||||
<meta name="twitter:description" content="{{ site.instance_tagline }}">
|
||||
<meta name="og:description" content="{{ site.instance_tagline }}">
|
||||
|
||||
{% block opengraph_images %}
|
||||
{% include 'snippets/opengraph_images.html' %}
|
||||
{% block opengraph %}
|
||||
{% include 'snippets/opengraph.html' %}
|
||||
{% endblock %}
|
||||
<meta name="twitter:image:alt" content="BookWyrm Logo">
|
||||
|
||||
{% block head_links %}{% endblock %}
|
||||
</head>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<td>
|
||||
<a href="{% url 'settings-users' %}?email=@{{ domain.domain }}">
|
||||
{% with user_count=domain.users.count %}
|
||||
{% blocktrans trimmed count conter=user_count with display_count=user_count|intcomma %}
|
||||
{% blocktrans trimmed count counter=user_count with display_count=user_count|intcomma %}
|
||||
{{ display_count }} user
|
||||
{% plural %}
|
||||
{{ display_count }} users
|
||||
|
@ -62,4 +62,3 @@
|
|||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</header>
|
||||
<div class="column is-narrow">
|
||||
<button type="button" class="button is-small" data-modal-open="{{ domain_modal }}">
|
||||
<span class="icon icon-pencil m-0-mobile" aria-hidden="treu"></span>
|
||||
<span class="icon icon-pencil m-0-mobile" aria-hidden="true"></span>
|
||||
<span class="is-sr-only-mobile">{% trans "Set display name" %}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -103,4 +103,3 @@
|
|||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
<form method="POST" action="{% url 'settings-themes-delete' theme.id %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-danger is-light is-small">
|
||||
<span class="icon icon-x" aria-hideen="true"></span>
|
||||
<span class="icon icon-x" aria-hidden="true"></span>
|
||||
<span>{% trans "Remove theme" %}</span>
|
||||
</button>
|
||||
</form>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
{% include 'user/books_header.html' with shelf=shelf %}
|
||||
{% endblock %}
|
||||
|
||||
{% block opengraph_images %}
|
||||
{% include 'snippets/opengraph_images.html' with image=user.preview_image %}
|
||||
{% block opengraph %}
|
||||
{% include 'snippets/opengraph.html' with image=user.preview_image %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
book: the Edition object this status is related to. Required unless the status is a reply
|
||||
draft: the content of an existing Status object to be edited (used in delete and redraft)
|
||||
uuid: a unique identifier used to make html "id" attributes unique and clarify javascript controls
|
||||
type: used for uniquely identifying the html elements when mutliple types of posts are available for a book, and usually the endpoint name that the form posts to
|
||||
type: used for uniquely identifying the html elements when multiple types of posts are available for a book, and usually the endpoint name that the form posts to
|
||||
reply_parent: the Status object this post will be in reply to, if applicable
|
||||
{% endcomment %}
|
||||
|
||||
{% block form_open %}
|
||||
{# default form tag syntax, can be overriddden #}
|
||||
{# default form tag syntax, can be overridden #}
|
||||
<form
|
||||
class="is-flex-grow-1{% if not no_script %} submit-status{% endif %}"
|
||||
name="{{ type }}"
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
type="radio"
|
||||
name="rating"
|
||||
value="{{ forloop.counter0 }}.5"
|
||||
{% if default_rating > 0 and default_rating >= forloop.counter0 %}checked{% endif %}
|
||||
{% if default_rating > 0 and default_rating > forloop.counter0 %}checked{% endif %}
|
||||
/>
|
||||
<input
|
||||
id="{{ type|slugify }}_book{{ book.id }}_star_{{ forloop.counter }}"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% load static %}
|
||||
|
||||
{% if preview_images_enabled is True %}
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
{% if image %}
|
||||
<meta name="twitter:image" content="{{ media_full_url }}{{ image }}">
|
||||
<meta name="og:image" content="{{ media_full_url }}{{ image }}">
|
||||
|
@ -9,6 +10,15 @@
|
|||
<meta name="og:image" content="{{ media_full_url }}{{ site.preview_image }}">
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:image" content="{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}">
|
||||
<meta name="og:image" content="{% if site.logo %}{{ media_full_url }}{{ site.logo }}{% else %}{% static "images/logo.png" %}{% endif %}">
|
||||
{% endif %}
|
||||
|
||||
<meta name="twitter:image:alt" content="BookWyrm Logo">
|
||||
|
||||
<meta name="twitter:title" content="{% if title %}{{ title }} - {% endif %}{{ site.name }}">
|
||||
<meta name="og:title" content="{% if title %}{{ title }} - {% endif %}{{ site.name }}">
|
||||
|
||||
<meta name="twitter:description" content="{% if description %}{{ description }}{% else %}{{ site.instance_tagline }}{% endif %}">
|
||||
<meta name="og:description" content="{% if description %}{{ description }}{% else %}{{ site.instance_tagline }}{% endif %}">
|
|
@ -13,7 +13,7 @@ Finish "<em>{{ book_title }}</em>"
|
|||
{% csrf_token %}
|
||||
<input type="hidden" name="id" value="{{ readthrough.id }}">
|
||||
<input type="hidden" name="reading_status" value="read">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block reading-dates %}
|
||||
|
|
|
@ -10,9 +10,9 @@ Start "<em>{{ book_title }}</em>"
|
|||
|
||||
{% block modal-form-open %}
|
||||
<form name="start-reading-{{ uuid }}" action="{% url 'reading-status' 'start' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="reading_status" value="reading">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
{% csrf_token %}
|
||||
{% endblock %}
|
||||
|
||||
{% block reading-dates %}
|
||||
|
|
|
@ -10,9 +10,9 @@ Want to Read "<em>{{ book_title }}</em>"
|
|||
|
||||
{% block modal-form-open %}
|
||||
<form name="want-to-read-{{ uuid }}" action="{% url 'reading-status' 'want' book.id %}" method="post" {% if not refresh %}class="submit-status"{% endif %}>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="reading_status" value="to-read">
|
||||
<input type="hidden" name="shelf" value="{{ move_from }}">
|
||||
{% csrf_token %}
|
||||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<input type="hidden" name="change-shelf-from" value="{{ current.identifier }}">
|
||||
<input type="hidden" name="shelf" value="{{ shelf.identifier }}">
|
||||
|
||||
<button class="button is-fullwidth is-small shelf-option is-radiusless has-background-body" type="submit" {% if shelf.identifier == current.identifier %}disabled{% endif %}>
|
||||
<span>
|
||||
{% include "snippets/translated_shelf_name.html" with shelf=shelf %}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
{% block title %}{{ user.display_name }}{% endblock %}
|
||||
|
||||
{% block opengraph_images %}
|
||||
{% include 'snippets/opengraph_images.html' with image=user.preview_image %}
|
||||
{% block opengraph %}
|
||||
{% include 'snippets/opengraph.html' with image=user.preview_image %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
from io import BytesIO
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
from dateutil.parser import parse
|
||||
from PIL import Image
|
||||
from django.core.files.base import ContentFile
|
||||
|
@ -10,6 +12,7 @@ from django.utils import timezone
|
|||
|
||||
from bookwyrm import models, settings
|
||||
from bookwyrm.models.book import isbn_10_to_13, isbn_13_to_10
|
||||
from bookwyrm.settings import ENABLE_THUMBNAIL_GENERATION
|
||||
|
||||
|
||||
class Book(TestCase):
|
||||
|
@ -101,6 +104,10 @@ class Book(TestCase):
|
|||
self.first_edition.save()
|
||||
self.assertEqual(self.first_edition.edition_rank, 1)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not ENABLE_THUMBNAIL_GENERATION,
|
||||
reason="Thumbnail generation disabled in settings",
|
||||
)
|
||||
def test_thumbnail_fields(self):
|
||||
"""Just hit them"""
|
||||
image_file = pathlib.Path(__file__).parent.joinpath(
|
||||
|
|
|
@ -3,6 +3,7 @@ import re
|
|||
from django.test import TestCase
|
||||
|
||||
from bookwyrm.utils import regex
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
|
||||
|
||||
class TestUtils(TestCase):
|
||||
|
@ -11,3 +12,20 @@ class TestUtils(TestCase):
|
|||
def test_regex(self):
|
||||
"""Regexes used throughout the app"""
|
||||
self.assertTrue(re.match(regex.DOMAIN, "xn--69aa8bzb.xn--y9a3aq"))
|
||||
|
||||
def test_valid_url_domain(self):
|
||||
"""Check with a valid URL"""
|
||||
self.assertEqual(
|
||||
validate_url_domain("https://your.domain.here/legit-book-url/"),
|
||||
"https://your.domain.here/legit-book-url/",
|
||||
)
|
||||
|
||||
def test_invalid_url_domain(self):
|
||||
"""Check with an invalid URL"""
|
||||
self.assertEqual(
|
||||
validate_url_domain("https://up-to-no-good.tld/bad-actor.exe"), "/"
|
||||
)
|
||||
|
||||
def test_default_url_domain(self):
|
||||
"""Check with a default URL"""
|
||||
self.assertEqual(validate_url_domain("/"), "/")
|
||||
|
|
|
@ -82,6 +82,7 @@ class EditBookViews(TestCase):
|
|||
form = forms.EditionForm(instance=self.book)
|
||||
form.data["title"] = ""
|
||||
form.data["last_edited_by"] = self.local_user.id
|
||||
form.data["cover-url"] = "http://local.host/cover.jpg"
|
||||
request = self.factory.post("", form.data)
|
||||
request.user = self.local_user
|
||||
|
||||
|
@ -91,6 +92,10 @@ class EditBookViews(TestCase):
|
|||
# Title is unchanged
|
||||
self.book.refresh_from_db()
|
||||
self.assertEqual(self.book.title, "Example Edition")
|
||||
# transient field values are set correctly
|
||||
self.assertEqual(
|
||||
result.context_data["cover_url"], "http://local.host/cover.jpg"
|
||||
)
|
||||
|
||||
def test_edit_book_add_author(self):
|
||||
"""lets a user edit a book with new authors"""
|
||||
|
@ -280,9 +285,14 @@ class EditBookViews(TestCase):
|
|||
form = forms.EditionForm(instance=self.book)
|
||||
form.data["title"] = ""
|
||||
form.data["last_edited_by"] = self.local_user.id
|
||||
form.data["cover-url"] = "http://local.host/cover.jpg"
|
||||
request = self.factory.post("", form.data)
|
||||
request.user = self.local_user
|
||||
|
||||
result = view(request)
|
||||
validate_html(result.render())
|
||||
self.assertEqual(result.status_code, 200)
|
||||
# transient field values are set correctly
|
||||
self.assertEqual(
|
||||
result.context_data["cover_url"], "http://local.host/cover.jpg"
|
||||
)
|
||||
|
|
19
bookwyrm/utils/validate.py
Normal file
19
bookwyrm/utils/validate.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
"""Validations"""
|
||||
from bookwyrm.settings import DOMAIN, USE_HTTPS
|
||||
|
||||
|
||||
def validate_url_domain(url, default="/"):
|
||||
"""Basic check that the URL starts with the instance domain name"""
|
||||
if not url:
|
||||
return default
|
||||
|
||||
if url in ("/", default):
|
||||
return url
|
||||
|
||||
protocol = "https://" if USE_HTTPS else "http://"
|
||||
origin = f"{protocol}{DOMAIN}"
|
||||
|
||||
if url.startswith(origin):
|
||||
return url
|
||||
|
||||
return default
|
|
@ -43,6 +43,7 @@ class EditBook(View):
|
|||
form = forms.EditionForm(request.POST, request.FILES, instance=book)
|
||||
|
||||
data = {"book": book, "form": form}
|
||||
ensure_transient_values_persist(request, data)
|
||||
if not form.is_valid():
|
||||
return TemplateResponse(request, "book/edit/edit_book.html", data)
|
||||
|
||||
|
@ -101,6 +102,8 @@ class CreateBook(View):
|
|||
"authors": authors,
|
||||
}
|
||||
|
||||
ensure_transient_values_persist(request, data)
|
||||
|
||||
if not form.is_valid():
|
||||
return TemplateResponse(request, "book/edit/edit_book.html", data)
|
||||
|
||||
|
@ -136,6 +139,11 @@ class CreateBook(View):
|
|||
return redirect(f"/book/{book.id}")
|
||||
|
||||
|
||||
def ensure_transient_values_persist(request, data):
|
||||
"""ensure that values of transient form fields persist when re-rendering the form"""
|
||||
data["cover_url"] = request.POST.get("cover-url")
|
||||
|
||||
|
||||
def add_authors(request, data):
|
||||
"""helper for adding authors"""
|
||||
add_author = [author for author in request.POST.getlist("add_author") if author]
|
||||
|
@ -150,7 +158,6 @@ def add_authors(request, data):
|
|||
data["confirm_mode"] = True
|
||||
# this isn't preserved because it isn't part of the form obj
|
||||
data["remove_authors"] = request.POST.getlist("remove_authors")
|
||||
data["cover_url"] = request.POST.get("cover-url")
|
||||
|
||||
for author in add_author:
|
||||
# filter out empty author fields
|
||||
|
|
|
@ -94,7 +94,7 @@ class List(View):
|
|||
return redirect(book_list.local_path)
|
||||
|
||||
|
||||
def get_list_suggestions(book_list, user, query=None):
|
||||
def get_list_suggestions(book_list, user, query=None, num_suggestions=5):
|
||||
"""What books might a user want to add to a list"""
|
||||
if query:
|
||||
# search for books
|
||||
|
@ -103,20 +103,26 @@ def get_list_suggestions(book_list, user, query=None):
|
|||
filters=[~Q(parent_work__editions__in=book_list.books.all())],
|
||||
)
|
||||
# just suggest whatever books are nearby
|
||||
suggestions = user.shelfbook_set.filter(~Q(book__in=book_list.books.all()))
|
||||
suggestions = [s.book for s in suggestions[:5]]
|
||||
if len(suggestions) < 5:
|
||||
suggestions += [
|
||||
suggestions = user.shelfbook_set.filter(
|
||||
~Q(book__in=book_list.books.all())
|
||||
).distinct()[:num_suggestions]
|
||||
suggestions = [s.book for s in suggestions[:num_suggestions]]
|
||||
if len(suggestions) < num_suggestions:
|
||||
others = [
|
||||
s.default_edition
|
||||
for s in models.Work.objects.filter(
|
||||
~Q(editions__in=book_list.books.all()),
|
||||
).order_by("-updated_date")[: 5 - len(suggestions)]
|
||||
)
|
||||
.distinct()
|
||||
.order_by("-updated_date")[:num_suggestions]
|
||||
]
|
||||
# get 'num_suggestions' unique items
|
||||
suggestions = list(set(suggestions + others))[:num_suggestions]
|
||||
return suggestions
|
||||
|
||||
|
||||
def sort_list(request, items):
|
||||
"""helper to handle the surprisngly involved sorting"""
|
||||
"""helper to handle the surprisingly involved sorting"""
|
||||
# sort_by shall be "order" unless a valid alternative is given
|
||||
sort_by = request.GET.get("sort_by", "order")
|
||||
if sort_by not in ("order", "title", "rating"):
|
||||
|
|
|
@ -12,6 +12,7 @@ from django.views.decorators.http import require_POST
|
|||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.views.shelf.shelf_actions import unshelve
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
from .status import CreateStatus
|
||||
from .helpers import get_edition, handle_reading_status, is_api_request
|
||||
from .helpers import load_date_in_user_tz_as_utc
|
||||
|
@ -42,6 +43,8 @@ class ReadingStatus(View):
|
|||
@transaction.atomic
|
||||
def post(self, request, status, book_id):
|
||||
"""Change the state of a book by shelving it and adding reading dates"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
identifier = {
|
||||
"want": models.Shelf.TO_READ,
|
||||
"start": models.Shelf.READING,
|
||||
|
@ -83,7 +86,7 @@ class ReadingStatus(View):
|
|||
if current_status_shelfbook.shelf.identifier != desired_shelf.identifier:
|
||||
current_status_shelfbook.delete()
|
||||
else: # It already was on the shelf
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
models.ShelfBook.objects.create(
|
||||
book=book, shelf=desired_shelf, user=request.user
|
||||
|
@ -121,7 +124,7 @@ class ReadingStatus(View):
|
|||
if is_api_request(request):
|
||||
return HttpResponse()
|
||||
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.db import IntegrityError, transaction
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.views.decorators.http import require_POST
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
|
||||
from bookwyrm import forms, models
|
||||
|
||||
|
@ -35,6 +36,8 @@ def delete_shelf(request, shelf_id):
|
|||
@transaction.atomic
|
||||
def shelve(request):
|
||||
"""put a book on a user's shelf"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
book = get_object_or_404(models.Edition, id=request.POST.get("book"))
|
||||
desired_shelf = get_object_or_404(
|
||||
request.user.shelf_set, identifier=request.POST.get("shelf")
|
||||
|
@ -64,13 +67,14 @@ def shelve(request):
|
|||
.first()
|
||||
)
|
||||
if current_read_status_shelfbook is not None:
|
||||
# If it is not already on the shelf
|
||||
if (
|
||||
current_read_status_shelfbook.shelf.identifier
|
||||
!= desired_shelf.identifier
|
||||
):
|
||||
current_read_status_shelfbook.delete()
|
||||
else: # It is already on the shelf
|
||||
return redirect("/")
|
||||
else:
|
||||
return redirect(next_step)
|
||||
|
||||
# create the new shelf-book entry
|
||||
models.ShelfBook.objects.create(
|
||||
|
@ -86,13 +90,16 @@ def shelve(request):
|
|||
# Might be good to alert, or reject the action?
|
||||
except IntegrityError:
|
||||
pass
|
||||
return redirect("/")
|
||||
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def unshelve(request, book_id=False):
|
||||
"""remove a book from a user's shelf"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
identity = book_id if book_id else request.POST.get("book")
|
||||
book = get_object_or_404(models.Edition, id=identity)
|
||||
shelf_book = get_object_or_404(
|
||||
|
@ -100,4 +107,4 @@ def unshelve(request, book_id=False):
|
|||
)
|
||||
shelf_book.raise_not_deletable(request.user)
|
||||
shelf_book.delete()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
|
|
@ -18,6 +18,7 @@ from django.views.decorators.http import require_POST
|
|||
from markdown import markdown
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.utils import regex, sanitizer
|
||||
from bookwyrm.utils.validate import validate_url_domain
|
||||
from .helpers import handle_remote_webfinger, is_api_request
|
||||
from .helpers import load_date_in_user_tz_as_utc
|
||||
|
||||
|
@ -58,6 +59,8 @@ class CreateStatus(View):
|
|||
# pylint: disable=too-many-branches
|
||||
def post(self, request, status_type, existing_status_id=None):
|
||||
"""create status of whatever type"""
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
created = not existing_status_id
|
||||
existing_status = None
|
||||
if existing_status_id:
|
||||
|
@ -80,7 +83,7 @@ class CreateStatus(View):
|
|||
if is_api_request(request):
|
||||
logger.exception(form.errors)
|
||||
return HttpResponseBadRequest()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
status = form.save(request, commit=False)
|
||||
status.ready = False
|
||||
|
@ -134,7 +137,7 @@ class CreateStatus(View):
|
|||
|
||||
if is_api_request(request):
|
||||
return HttpResponse()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
|
@ -167,6 +170,8 @@ def update_progress(request, book_id): # pylint: disable=unused-argument
|
|||
def edit_readthrough(request):
|
||||
"""can't use the form because the dates are too finnicky"""
|
||||
# TODO: remove this, it duplicates the code in the ReadThrough view
|
||||
next_step = request.META.get("HTTP_REFERER")
|
||||
next_step = validate_url_domain(next_step, "/")
|
||||
readthrough = get_object_or_404(models.ReadThrough, id=request.POST.get("id"))
|
||||
|
||||
readthrough.start_date = load_date_in_user_tz_as_utc(
|
||||
|
@ -198,7 +203,7 @@ def edit_readthrough(request):
|
|||
|
||||
if is_api_request(request):
|
||||
return HttpResponse()
|
||||
return redirect("/")
|
||||
return redirect(next_step)
|
||||
|
||||
|
||||
def find_mentions(user, content):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue