diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 5af7c455..5ab90895 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -225,7 +225,7 @@ class LinkDomainForm(CustomForm): class FileLinkForm(CustomForm): class Meta: model = models.FileLink - fields = ["url", "filetype", "book", "added_by"] + fields = ["url", "filetype", "availability", "book", "added_by"] class EditionForm(CustomForm): diff --git a/bookwyrm/migrations/0129_auto_20220117_1716.py b/bookwyrm/migrations/0129_auto_20220117_1716.py new file mode 100644 index 00000000..6b05fd27 --- /dev/null +++ b/bookwyrm/migrations/0129_auto_20220117_1716.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.10 on 2022-01-17 17:16 + +import bookwyrm.models.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0128_merge_0126_auto_20220112_2315_0127_auto_20220110_2211"), + ] + + operations = [ + migrations.AddField( + model_name="filelink", + name="availability", + field=bookwyrm.models.fields.CharField( + choices=[ + ("free", "Free"), + ("purchase", "Purchasable"), + ("loan", "Available for loan"), + ], + default="free", + max_length=100, + ), + ), + migrations.AlterField( + model_name="filelink", + name="filetype", + field=bookwyrm.models.fields.CharField(max_length=50), + ), + ] diff --git a/bookwyrm/models/link.py b/bookwyrm/models/link.py index be7c104f..0e4148dd 100644 --- a/bookwyrm/models/link.py +++ b/bookwyrm/models/link.py @@ -47,13 +47,23 @@ class Link(ActivitypubMixin, BookWyrmModel): return super().save(*args, **kwargs) +AvailabilityChoices = [ + ("free", _("Free")), + ("purchase", _("Purchasable")), + ("loan", _("Available for loan")), +] + + class FileLink(Link): """a link to a file""" book = models.ForeignKey( "Book", on_delete=models.CASCADE, related_name="file_links", null=True ) - filetype = fields.CharField(max_length=5, activitypub_field="mediaType") + filetype = fields.CharField(max_length=50, activitypub_field="mediaType") + availability = fields.CharField( + max_length=100, choices=AvailabilityChoices, default="free" + ) StatusChoices = [ diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 85be4ce5..c018718c 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -14,7 +14,7 @@ VERSION = "0.2.0" PAGE_LENGTH = env("PAGE_LENGTH", 15) DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English") -JS_CACHE = "a47cc2ca" +JS_CACHE = "76c5ff1f" # email EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend") diff --git a/bookwyrm/static/css/bookwyrm.css b/bookwyrm/static/css/bookwyrm.css index 18c13671..f05ea3c9 100644 --- a/bookwyrm/static/css/bookwyrm.css +++ b/bookwyrm/static/css/bookwyrm.css @@ -722,6 +722,7 @@ ol.ordered-list li::before { .overflow-wrap-anywhere { overflow-wrap: anywhere; + min-width: 10em; } /* Threads diff --git a/bookwyrm/static/js/autocomplete.js b/bookwyrm/static/js/autocomplete.js index 89610083..84474e43 100644 --- a/bookwyrm/static/js/autocomplete.js +++ b/bookwyrm/static/js/autocomplete.js @@ -42,6 +42,8 @@ function getSuggestions(input, trie) { // Follow the trie through the provided input + input = input.toLowerCase(); + input.split("").forEach((letter) => { if (!trie) { return; @@ -160,6 +162,23 @@ const tries = { }, }, }, + r: { + i: { + n: { + t: { + " ": { + b: { + o: { + o: { + k: "Print book", + }, + }, + }, + }, + }, + }, + }, + }, }, }, }; diff --git a/bookwyrm/templates/book/file_links/add_link_modal.html b/bookwyrm/templates/book/file_links/add_link_modal.html index dfc222ee..0002b82b 100644 --- a/bookwyrm/templates/book/file_links/add_link_modal.html +++ b/bookwyrm/templates/book/file_links/add_link_modal.html @@ -30,7 +30,7 @@ +
+ +
+ {{ file_link_form.availability }} +
+
{% endblock %} diff --git a/bookwyrm/templates/book/file_links/edit_links.html b/bookwyrm/templates/book/file_links/edit_links.html index c56f97a6..8dad6c40 100644 --- a/bookwyrm/templates/book/file_links/edit_links.html +++ b/bookwyrm/templates/book/file_links/edit_links.html @@ -33,9 +33,10 @@ {% trans "Added by" %} {% trans "Filetype" %} {% trans "Domain" %} - {% trans "Actions" %} + {% trans "Status" %} + {% trans "Actions" %} - {% for link in book.file_links.all %} + {% for link in links %} {{ link.url }} @@ -47,13 +48,44 @@ {{ link.filelink.filetype }} - {{ link.domain.name }} ({{ link.domain.get_status_display }}) + {{ link.domain.name }}

{% trans "Report spam" %}

-
+ {% with status=link.domain.status %} + + + + {{ link.domain.get_status_display }} + + + {% endwith %} + + + + {% csrf_token %} + + + + +
+
+
+ {{ link.form.availability }} +
+
+
+ +
+
+
+ + +
{% csrf_token %}
diff --git a/bookwyrm/templates/book/file_links/links.html b/bookwyrm/templates/book/file_links/links.html index 10a6da2f..25e0ba89 100644 --- a/bookwyrm/templates/book/file_links/links.html +++ b/bookwyrm/templates/book/file_links/links.html @@ -30,6 +30,12 @@
  • {{ link.name }} ({{ link.filetype }}) + + {% if link.availability != "free" %} +

    + {{ link.get_availability_display }} +

    + {% endif %}
  • {% endfor %} @@ -46,7 +52,7 @@ {% trans "Edit links" %} -{% include 'book/file_links/add_link_modal.html' with book=book id="add-links" %} {% endif %} +{% include 'book/file_links/add_link_modal.html' with book=book id="add-links" %} {% endif %} diff --git a/bookwyrm/templates/settings/link_domains/link_domains.html b/bookwyrm/templates/settings/link_domains/link_domains.html index 3f2d6097..81235b33 100644 --- a/bookwyrm/templates/settings/link_domains/link_domains.html +++ b/bookwyrm/templates/settings/link_domains/link_domains.html @@ -42,7 +42,7 @@
    diff --git a/bookwyrm/tests/views/books/test_links.py b/bookwyrm/tests/views/books/test_links.py index 9e051926..2aee5aed 100644 --- a/bookwyrm/tests/views/books/test_links.py +++ b/bookwyrm/tests/views/books/test_links.py @@ -67,6 +67,7 @@ class LinkViews(TestCase): form.data["filetype"] = "HTML" form.data["book"] = self.book.id form.data["added_by"] = self.local_user.id + form.data["availability"] = "loan" request = self.factory.post("", form.data) request.user = self.local_user @@ -87,3 +88,59 @@ class LinkViews(TestCase): self.book.refresh_from_db() self.assertEqual(self.book.file_links.first(), link) + + def test_book_links(self): + """there are so many views, this just makes sure it LOADS""" + view = views.BookFileLinks.as_view() + models.FileLink.objects.create( + book=self.book, + added_by=self.local_user, + url="https://www.hello.com", + ) + request = self.factory.get("") + request.user = self.local_user + result = view(request, self.book.id) + self.assertEqual(result.status_code, 200) + validate_html(result.render()) + + def test_book_links_post(self): + """there are so many views, this just makes sure it LOADS""" + link = models.FileLink.objects.create( + book=self.book, + added_by=self.local_user, + url="https://www.hello.com", + ) + view = views.BookFileLinks.as_view() + form = forms.FileLinkForm() + form.data["url"] = link.url + form.data["filetype"] = "HTML" + form.data["book"] = self.book.id + form.data["added_by"] = self.local_user.id + form.data["availability"] = "loan" + + request = self.factory.post("", form.data) + request.user = self.local_user + view(request, self.book.id, link.id) + + link.refresh_from_db() + self.assertEqual(link.filetype, "HTML") + self.assertEqual(link.availability, "loan") + + def test_delete_link(self): + """remove a link""" + link = models.FileLink.objects.create( + book=self.book, + added_by=self.local_user, + url="https://www.hello.com", + ) + form = forms.FileLinkForm() + form.data["url"] = "https://www.example.com" + form.data["filetype"] = "HTML" + form.data["book"] = self.book.id + form.data["added_by"] = self.local_user.id + form.data["availability"] = "loan" + + request = self.factory.post("", form.data) + request.user = self.local_user + views.delete_link(request, self.book.id, link.id) + self.assertFalse(models.FileLink.objects.exists()) diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 18390d54..ac25e878 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -485,10 +485,15 @@ urlpatterns = [ rf"{BOOK_PATH}/filelink/?$", views.BookFileLinks.as_view(), name="file-link" ), re_path( - rf"{BOOK_PATH}/filelink/(?P\d+)/delete/?$", + rf"{BOOK_PATH}/filelink/(?P\d+)/?$", views.BookFileLinks.as_view(), name="file-link", ), + re_path( + rf"{BOOK_PATH}/filelink/(?P\d+)/delete/?$", + views.delete_link, + name="file-link-delete", + ), re_path( rf"{BOOK_PATH}/filelink/add/?$", views.AddFileLink.as_view(), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 2ec501de..3f57c274 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -37,7 +37,7 @@ from .books.books import ( from .books.books import update_book_from_remote from .books.edit_book import EditBook, ConfirmEditBook from .books.editions import Editions, switch_edition -from .books.links import BookFileLinks, AddFileLink +from .books.links import BookFileLinks, AddFileLink, delete_link # landing from .landing.about import about, privacy, conduct diff --git a/bookwyrm/views/admin/link_domains.py b/bookwyrm/views/admin/link_domains.py index 564ea896..5f9ec6c0 100644 --- a/bookwyrm/views/admin/link_domains.py +++ b/bookwyrm/views/admin/link_domains.py @@ -20,9 +20,9 @@ class LinkDomain(View): def get(self, request, status="pending"): """view pending domains""" data = { - "domains": models.LinkDomain.objects.filter(status=status).prefetch_related( - "links" - ), + "domains": models.LinkDomain.objects.filter(status=status) + .prefetch_related("links") + .order_by("-created_date"), "counts": { "pending": models.LinkDomain.objects.filter(status="pending").count(), "approved": models.LinkDomain.objects.filter(status="approved").count(), diff --git a/bookwyrm/views/books/links.py b/bookwyrm/views/books/links.py index 989ca9c4..51621023 100644 --- a/bookwyrm/views/books/links.py +++ b/bookwyrm/views/books/links.py @@ -5,28 +5,49 @@ from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse from django.views import View from django.utils.decorators import method_decorator +from django.views.decorators.http import require_POST from bookwyrm import forms, models # pylint: disable=no-self-use +@method_decorator(login_required, name="dispatch") +@method_decorator( + permission_required("bookwyrm.edit_book", raise_exception=True), name="dispatch" +) class BookFileLinks(View): """View all links""" def get(self, request, book_id): """view links""" book = get_object_or_404(models.Edition, id=book_id) - return TemplateResponse( - request, "book/file_links/edit_links.html", {"book": book} - ) + links = book.file_links.order_by("domain__status", "created_date") + annotated_links = [] + for link in links.all(): + link.form = forms.FileLinkForm(instance=link) + annotated_links.append(link) + + data = {"book": book, "links": annotated_links} + return TemplateResponse(request, "book/file_links/edit_links.html", data) def post(self, request, book_id, link_id): - """delete link""" + """Edit a link""" link = get_object_or_404(models.FileLink, id=link_id, book=book_id) - link.delete() + form = forms.FileLinkForm(request.POST, instance=link) + form.save() return self.get(request, book_id) +@require_POST +@login_required +# pylint: disable=unused-argument +def delete_link(request, book_id, link_id): + """delete link""" + link = get_object_or_404(models.FileLink, id=link_id, book=book_id) + link.delete() + return redirect("file-link", book_id) + + @method_decorator(login_required, name="dispatch") @method_decorator( permission_required("bookwyrm.edit_book", raise_exception=True), name="dispatch"