From cb187c880e7f418675ff0a14b01c171e4a20f868 Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Wed, 16 Nov 2022 12:38:49 +0100 Subject: [PATCH 01/62] Quote ports in docker-compose Per sepcification in compose-file v3 ports are in quotes. https://docs.docker.com/compose/compose-file/compose-file-v3/ --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index c654374b9..18647aea6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: nginx:latest restart: unless-stopped ports: - - 1333:80 + - "1333:80" depends_on: - web networks: @@ -36,7 +36,7 @@ services: networks: - main ports: - - 8000 + - "8000" redis_activity: image: redis command: redis-server --requirepass ${REDIS_ACTIVITY_PASSWORD} --appendonly yes --port ${REDIS_ACTIVITY_PORT} From c29256708abe16e483a0e7af26ae9bbf101b19dd Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Wed, 16 Nov 2022 20:30:06 +0100 Subject: [PATCH 02/62] show otp_secret when setting up 2fa solves #2389 --- bookwyrm/templates/preferences/2fa.html | 79 +++++++++++++++++++ bookwyrm/views/preferences/two_factor_auth.py | 9 ++- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/preferences/2fa.html b/bookwyrm/templates/preferences/2fa.html index b0703bc4a..397cf7d71 100644 --- a/bookwyrm/templates/preferences/2fa.html +++ b/bookwyrm/templates/preferences/2fa.html @@ -46,6 +46,58 @@
{{ qrcode | safe }}
+
+ + {{ form.otp }} + {% include 's{% extends 'preferences/layout.html' %} +{% load i18n %} + +{% block title %}{% trans "Two Factor Authentication" %}{% endblock %} + +{% block header %} +{% trans "Two Factor Authentication" %} +{% endblock %} + +{% block panel %} +
+ {% if success %} +
+ + + {% trans "Successfully updated 2FA settings" %} + +
+ {% endif %} + {% if backup_codes %} +
+

Backup codes

+
+

{% trans "Write down or copy and paste these codes somewhere safe." %}

+

{% trans "You must use them in order, and they will not be displayed again." %}

+
+
    + {% for code in backup_codes %} +
  • {{ code }}
  • + {% endfor%} +
+
+ {% elif request.user.two_factor_auth %} +
+

{% trans "Two Factor Authentication is active on your account." %}

+ {% trans "Disable 2FA" %} +
+
+

{% trans "You can generate backup codes to use in case you do not have access to your authentication app. If you generate new codes, any backup codes previously generated will no longer work." %}

+ {% trans "Generate backup codes" %} +
+ {% elif password_confirmed %} +
+ {% csrf_token %} +

{% trans "Scan the QR code with your authentication app and then enter the code from your app below to confirm your app is set up." %}

+
+
+
{{ qrcode | safe }}
+
{{ code | safe }}
{{ form.otp }} @@ -76,3 +128,30 @@ {% endif %}
{% endblock %} +nippets/form_errors.html' with errors_list=form.otp.errors id="desc_otp" %} +
+ +
+
+
+ {% else %} +

+ {% trans "You can make your account more secure by using Two Factor Authentication (2FA). This will require you to enter a one-time code using a phone app like Authy, Google Authenticator or Microsoft Authenticator each time you log in." %} +

+

{% trans "Confirm your password to begin setting up 2FA." %}

+
+
+
+ {% csrf_token %} +
+ + {{ form.password }} + {% include 'snippets/form_errors.html' with errors_list=form.password.errors id="desc_password" %} +
+ +
+
+
+ {% endif %} +
+{% endblock %} diff --git a/bookwyrm/views/preferences/two_factor_auth.py b/bookwyrm/views/preferences/two_factor_auth.py index f3b04eb3c..192cdaff7 100644 --- a/bookwyrm/views/preferences/two_factor_auth.py +++ b/bookwyrm/views/preferences/two_factor_auth.py @@ -35,10 +35,12 @@ class Edit2FA(View): if not form.is_valid(): data = {"form": form} return TemplateResponse(request, "preferences/2fa.html", data) + data = self.create_qr_code(request.user) qr_form = forms.Confirm2FAForm() data = { "password_confirmed": True, - "qrcode": self.create_qr_code(request.user), + "qrcode": data[0], + "code": data[1], "form": qr_form, } return TemplateResponse(request, "preferences/2fa.html", data) @@ -57,7 +59,10 @@ class Edit2FA(View): qr_code.add_data(provisioning_url) qr_code.make(fit=True) img = qr_code.make_image(attrib={"fill": "black"}) - return str(img.to_string(), "utf-8") # to_string() returns a byte string + return [ + str(img.to_string(), "utf-8"), + otp_secret, + ] # to_string() returns a byte string @method_decorator(login_required, name="dispatch") From a98dbb97a5d80507cc85622b202ed98b536ee8c9 Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Wed, 16 Nov 2022 20:32:40 +0100 Subject: [PATCH 03/62] fix error fix my copy paste error -.- --- bookwyrm/templates/preferences/2fa.html | 78 ------------------------- 1 file changed, 78 deletions(-) diff --git a/bookwyrm/templates/preferences/2fa.html b/bookwyrm/templates/preferences/2fa.html index 397cf7d71..9d58d7cc1 100644 --- a/bookwyrm/templates/preferences/2fa.html +++ b/bookwyrm/templates/preferences/2fa.html @@ -7,57 +7,6 @@ {% trans "Two Factor Authentication" %} {% endblock %} -{% block panel %} -
- {% if success %} -
- - - {% trans "Successfully updated 2FA settings" %} - -
- {% endif %} - {% if backup_codes %} -
-

Backup codes

-
-

{% trans "Write down or copy and paste these codes somewhere safe." %}

-

{% trans "You must use them in order, and they will not be displayed again." %}

-
-
    - {% for code in backup_codes %} -
  • {{ code }}
  • - {% endfor%} -
-
- {% elif request.user.two_factor_auth %} -
-

{% trans "Two Factor Authentication is active on your account." %}

- {% trans "Disable 2FA" %} -
-
-

{% trans "You can generate backup codes to use in case you do not have access to your authentication app. If you generate new codes, any backup codes previously generated will no longer work." %}

- {% trans "Generate backup codes" %} -
- {% elif password_confirmed %} -
- {% csrf_token %} -

{% trans "Scan the QR code with your authentication app and then enter the code from your app below to confirm your app is set up." %}

-
-
-
{{ qrcode | safe }}
-
- - {{ form.otp }} - {% include 's{% extends 'preferences/layout.html' %} -{% load i18n %} - -{% block title %}{% trans "Two Factor Authentication" %}{% endblock %} - -{% block header %} -{% trans "Two Factor Authentication" %} -{% endblock %} - {% block panel %}
{% if success %} @@ -128,30 +77,3 @@ {% endif %}
{% endblock %} -nippets/form_errors.html' with errors_list=form.otp.errors id="desc_otp" %} -
- -
-
-
- {% else %} -

- {% trans "You can make your account more secure by using Two Factor Authentication (2FA). This will require you to enter a one-time code using a phone app like Authy, Google Authenticator or Microsoft Authenticator each time you log in." %} -

-

{% trans "Confirm your password to begin setting up 2FA." %}

-
-
-
- {% csrf_token %} -
- - {{ form.password }} - {% include 'snippets/form_errors.html' with errors_list=form.password.errors id="desc_password" %} -
- -
-
-
- {% endif %} -
-{% endblock %} From 170aa7460aab37d79dc3e11978a44e49a9d3a102 Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Wed, 16 Nov 2022 20:35:28 +0100 Subject: [PATCH 04/62] Update 2fa.html Or now, because obviously I can not write HTML^^ --- bookwyrm/templates/preferences/2fa.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/preferences/2fa.html b/bookwyrm/templates/preferences/2fa.html index 9d58d7cc1..b6bce604f 100644 --- a/bookwyrm/templates/preferences/2fa.html +++ b/bookwyrm/templates/preferences/2fa.html @@ -46,7 +46,7 @@
{{ qrcode | safe }}
-
{{ code | safe }} +
{{ code | safe }}
{{ form.otp }} From 6dd671ae5a79a718ed2238088f4f6d7289375089 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 11:23:48 -0800 Subject: [PATCH 05/62] Fixes user follow cache --- bookwyrm/templatetags/interaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templatetags/interaction.py b/bookwyrm/templatetags/interaction.py index 39bf32b63..9c73aa1af 100644 --- a/bookwyrm/templatetags/interaction.py +++ b/bookwyrm/templatetags/interaction.py @@ -42,7 +42,7 @@ def get_relationship(context, user_object): """caches the relationship between the logged in user and another user""" user = context["request"].user return get_or_set( - f"relationship-{user.id}-{user_object.id}", + f"cached-relationship-{user.id}-{user_object.id}", get_relationship_name, user, user_object, From 77d96bf2455633f8121549d772029eefba7b44f1 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 12:03:59 -0800 Subject: [PATCH 06/62] Only offer RSS feeds for local users --- bookwyrm/templates/user/user.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookwyrm/templates/user/user.html b/bookwyrm/templates/user/user.html index 8e61d525d..d67f42fc3 100755 --- a/bookwyrm/templates/user/user.html +++ b/bookwyrm/templates/user/user.html @@ -64,12 +64,14 @@

{% trans "User Activity" %}

+ {% if user.local %} + {% endif %}
{% for activity in activities %}
From b37a4322de6da3ff8254088e7ed18b3bf943ba55 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 12:35:19 -0800 Subject: [PATCH 07/62] Change log level to info for connector exceptions These errors in resolve_remote_id aren't really errors, they're routine problems that we can expect from dealing with the outside world, like a connection timeout, a server being down, a server being blocked, et cetera. It's cluttering up the logs and causing unnecessary worry. --- bookwyrm/activitypub/base_activity.py | 2 +- bookwyrm/connectors/abstract_connector.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index e942c9aeb..fa845f124 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -271,7 +271,7 @@ def resolve_remote_id( try: data = get_data(remote_id) except ConnectorException: - logger.exception("Could not connect to host for remote_id: %s", remote_id) + logger.info("Could not connect to host for remote_id: %s", remote_id) return None # determine the model implicitly, if not provided diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index c1ee7fe78..8ae93926a 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -222,7 +222,7 @@ def dict_from_mappings(data, mappings): return result -def get_data(url, params=None, timeout=10): +def get_data(url, params=None, timeout=settings.QUERY_TIMEOUT): """wrapper for request.get""" # check if the url is blocked raise_not_valid_url(url) From 474da162ba2437c3ddc0d1dbb7058a853d907820 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 13:32:05 -0800 Subject: [PATCH 08/62] Uses correct css paths in embeds --- bookwyrm/templates/embed-layout.html | 5 ++--- bookwyrm/templates/lists/embed-list.html | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bookwyrm/templates/embed-layout.html b/bookwyrm/templates/embed-layout.html index 233ba387f..6a8d77016 100644 --- a/bookwyrm/templates/embed-layout.html +++ b/bookwyrm/templates/embed-layout.html @@ -1,14 +1,13 @@ {% load layout %} {% load i18n %} +{% load sass_tags %} {% load static %} {% block title %}BookWyrm{% endblock %} - {{ site.name }} - - - + diff --git a/bookwyrm/templates/lists/embed-list.html b/bookwyrm/templates/lists/embed-list.html index 186681670..d9a50a464 100644 --- a/bookwyrm/templates/lists/embed-list.html +++ b/bookwyrm/templates/lists/embed-list.html @@ -5,7 +5,9 @@ {% load group_tags %} {% load markdown %} -{% block title %}{% blocktrans with list_name=list.name owner=list.user.display_name %}{{ list_name }}, a list by {{owner}}{% endblocktrans %}{% endblock title %} +{% block title %}{% blocktrans trimmed with list_name=list.name owner=list.user.display_name %} +{{ list_name }}, a list by {{owner}} +{% endblocktrans %}{% endblock title %} {% block content %}
From 7c7c0e1a9326672a8dce3621c410d22cf9c01caa Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 14:22:45 -0800 Subject: [PATCH 09/62] Admin UI to enable and disable importing --- .../0166_sitesettings_imports_enabled.py | 18 +++++++ bookwyrm/models/site.py | 3 ++ .../templates/settings/imports/imports.html | 48 +++++++++++++++++++ bookwyrm/urls.py | 10 ++++ bookwyrm/views/__init__.py | 2 +- bookwyrm/views/admin/imports.py | 23 +++++++++ 6 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 bookwyrm/migrations/0166_sitesettings_imports_enabled.py diff --git a/bookwyrm/migrations/0166_sitesettings_imports_enabled.py b/bookwyrm/migrations/0166_sitesettings_imports_enabled.py new file mode 100644 index 000000000..ccf4ef374 --- /dev/null +++ b/bookwyrm/migrations/0166_sitesettings_imports_enabled.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2022-11-17 21:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0165_alter_inviterequest_answer"), + ] + + operations = [ + migrations.AddField( + model_name="sitesettings", + name="imports_enabled", + field=models.BooleanField(default=True), + ), + ] diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index 3c1494204..9e97ede9a 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -86,6 +86,9 @@ class SiteSettings(SiteModel): admin_email = models.EmailField(max_length=255, null=True, blank=True) footer_item = models.TextField(null=True, blank=True) + # controls + imports_enabled = models.BooleanField(default=True) + field_tracker = FieldTracker(fields=["name", "instance_tagline", "logo"]) @classmethod diff --git a/bookwyrm/templates/settings/imports/imports.html b/bookwyrm/templates/settings/imports/imports.html index c6e02f0e1..135af34ed 100644 --- a/bookwyrm/templates/settings/imports/imports.html +++ b/bookwyrm/templates/settings/imports/imports.html @@ -11,6 +11,54 @@ {% block panel %} +
+ {% if site.imports_enabled %} +
+ + + {% trans "Disable starting new imports" %} + + + +
+
+ {% trans "This is only intended to be used when things have gone very wrong with imports and you need to pause the feature while addressing issues." %} + {% trans "While imports are disabled, users will not be allowed to start new imports, but existing imports will not be effected." %} +
+ {% csrf_token %} +
+ +
+
+
+ {% else %} +
+
+ {% trans "Users are currently unable to start new imports" %} +
+ {% csrf_token %} +
+ +
+
+ {% endif %} +
+
    diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index a1e0ef844..daf05e10e 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -301,6 +301,16 @@ urlpatterns = [ views.ImportList.as_view(), name="settings-imports-complete", ), + re_path( + r"^settings/imports/disable/?$", + views.disable_imports, + name="settings-imports-disable", + ), + re_path( + r"^settings/imports/enable/?$", + views.enable_imports, + name="settings-imports-enable", + ), re_path( r"^settings/celery/?$", views.CeleryStatus.as_view(), name="settings-celery" ), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index e5b772136..21e33450c 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -10,7 +10,7 @@ from .admin.federation import Federation, FederatedServer from .admin.federation import AddFederatedServer, ImportServerBlocklist from .admin.federation import block_server, unblock_server, refresh_server from .admin.email_blocklist import EmailBlocklist -from .admin.imports import ImportList +from .admin.imports import ImportList, disable_imports, enable_imports from .admin.ip_blocklist import IPBlocklist from .admin.invite import ManageInvites, Invite, InviteRequest from .admin.invite import ManageInviteRequests, ignore_invite_request diff --git a/bookwyrm/views/admin/imports.py b/bookwyrm/views/admin/imports.py index cdb8af751..fe04a0f2b 100644 --- a/bookwyrm/views/admin/imports.py +++ b/bookwyrm/views/admin/imports.py @@ -5,6 +5,7 @@ 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 models from bookwyrm.settings import PAGE_LENGTH @@ -53,3 +54,25 @@ class ImportList(View): import_job = get_object_or_404(models.ImportJob, id=import_id) import_job.stop_job() return redirect("settings-imports") + + +@require_POST +@permission_required("bookwyrm.edit_instance_settings", raise_exception=True) +# pylint: disable=unused-argument +def disable_imports(request): + """When you just need people to please stop starting imports""" + site = models.SiteSettings.objects.get() + site.imports_enabled = False + site.save(update_fields=["imports_enabled"]) + return redirect("settings-imports") + + +@require_POST +@permission_required("bookwyrm.edit_instance_settings", raise_exception=True) +# pylint: disable=unused-argument +def enable_imports(request): + """When you just need people to please stop starting imports""" + site = models.SiteSettings.objects.get() + site.imports_enabled = True + site.save(update_fields=["imports_enabled"]) + return redirect("settings-imports") From b1c67810368c9c87e64f1efa2b82c53469912c66 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 14:38:05 -0800 Subject: [PATCH 10/62] Fixes collecting list of admins --- bookwyrm/models/user.py | 7 ++++--- bookwyrm/templates/about/about.html | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index 5f7b00d87..e48d86572 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -244,9 +244,10 @@ class User(OrderedCollectionPageMixin, AbstractUser): def admins(cls): """Get a queryset of the admins for this instance""" return cls.objects.filter( - models.Q(user_permissions__name__in=["moderate_user", "moderate_post"]) - | models.Q(is_superuser=True) - ) + models.Q(groups__name__in=["moderator", "admin"]) + | models.Q(is_superuser=True), + is_active=True, + ).distinct() def update_active_date(self): """this user is here! they are doing things!""" diff --git a/bookwyrm/templates/about/about.html b/bookwyrm/templates/about/about.html index 481ecda99..c446e0cf2 100644 --- a/bookwyrm/templates/about/about.html +++ b/bookwyrm/templates/about/about.html @@ -11,7 +11,7 @@ {% block about_content %} {# seven day cache #} -{% cache 604800 about_page %} +{% cache 604800 about_page_superlatives %} {% get_book_superlatives as superlatives %}
    @@ -97,6 +97,7 @@

    +{% endcache %}
    @@ -145,5 +146,4 @@
-{% endcache %} {% endblock %} From 34f05c135b1496b863bfe83d16f28429e6d4455f Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 14:53:22 -0800 Subject: [PATCH 11/62] Adds unit tests for list of admins method --- bookwyrm/tests/models/test_user_model.py | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/bookwyrm/tests/models/test_user_model.py b/bookwyrm/tests/models/test_user_model.py index 37e2192be..78c5523d6 100644 --- a/bookwyrm/tests/models/test_user_model.py +++ b/bookwyrm/tests/models/test_user_model.py @@ -1,10 +1,12 @@ """ testing models """ import json from unittest.mock import patch +from django.contrib.auth.models import Group from django.test import TestCase import responses from bookwyrm import models +from bookwyrm.management.commands import initdb from bookwyrm.settings import USE_HTTPS, DOMAIN # pylint: disable=missing-class-docstring @@ -12,6 +14,7 @@ from bookwyrm.settings import USE_HTTPS, DOMAIN class User(TestCase): protocol = "https://" if USE_HTTPS else "http://" + # pylint: disable=invalid-name def setUp(self): with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( "bookwyrm.activitystreams.populate_stream_task.delay" @@ -25,6 +28,17 @@ class User(TestCase): name="hi", bookwyrm_user=False, ) + self.another_user = models.User.objects.create_user( + f"rat@{DOMAIN}", + "rat@rat.rat", + "ratword", + local=True, + localname="rat", + name="hi", + bookwyrm_user=False, + ) + initdb.init_groups() + initdb.init_permissions() def test_computed_fields(self): """username instead of id here""" @@ -176,3 +190,41 @@ class User(TestCase): self.assertEqual(activity["type"], "Delete") self.assertEqual(activity["object"], self.user.remote_id) self.assertFalse(self.user.is_active) + + def test_admins_no_admins(self): + """list of admins""" + result = models.User.admins() + self.assertFalse(result.exists()) + + def test_admins_superuser(self): + """list of admins""" + self.user.is_superuser = True + self.user.save(broadcast=False, update_fields=["is_superuser"]) + result = models.User.admins() + self.assertEqual(result.count(), 1) + self.assertEqual(result.first(), self.user) + + def test_admins_superuser_and_mod(self): + """list of admins""" + self.user.is_superuser = True + self.user.save(broadcast=False, update_fields=["is_superuser"]) + group = Group.objects.get(name="moderator") + self.another_user.groups.set([group]) + + results = models.User.admins() + self.assertEqual(results.count(), 2) + self.assertTrue(results.filter(id=self.user.id).exists()) + self.assertTrue(results.filter(id=self.another_user.id).exists()) + + def test_admins_deleted_mod(self): + """list of admins""" + self.user.is_superuser = True + self.user.save(broadcast=False, update_fields=["is_superuser"]) + group = Group.objects.get(name="moderator") + self.another_user.groups.set([group]) + self.another_user.is_active = False + self.another_user.save(broadcast=False, update_fields=None) + + results = models.User.admins() + self.assertEqual(results.count(), 1) + self.assertEqual(results.first(), self.user) From 46b663b139b8bf27352583e77a5af829131f5a4c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 15:10:11 -0800 Subject: [PATCH 12/62] Rename new user to avoid duplication --- bookwyrm/tests/models/test_user_model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bookwyrm/tests/models/test_user_model.py b/bookwyrm/tests/models/test_user_model.py index 78c5523d6..80b7a76d4 100644 --- a/bookwyrm/tests/models/test_user_model.py +++ b/bookwyrm/tests/models/test_user_model.py @@ -29,11 +29,11 @@ class User(TestCase): bookwyrm_user=False, ) self.another_user = models.User.objects.create_user( - f"rat@{DOMAIN}", - "rat@rat.rat", - "ratword", + f"nutria@{DOMAIN}", + "nutria@nutria.nutria", + "nutriaword", local=True, - localname="rat", + localname="nutria", name="hi", bookwyrm_user=False, ) From 28567e2d8eb0ec69c100d7050e2f16e4f5ced32c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 15:19:42 -0800 Subject: [PATCH 13/62] Disable imports in the UI and view --- bookwyrm/templates/import/import.html | 157 ++++++++++++++------------ bookwyrm/views/imports/import_data.py | 5 + 2 files changed, 89 insertions(+), 73 deletions(-) diff --git a/bookwyrm/templates/import/import.html b/bookwyrm/templates/import/import.html index a2924703c..141e5671e 100644 --- a/bookwyrm/templates/import/import.html +++ b/bookwyrm/templates/import/import.html @@ -8,83 +8,94 @@

{% trans "Import Books" %}

- {% if recent_avg_hours or recent_avg_minutes %} -
-

- {% if recent_avg_hours %} - {% blocktrans trimmed with hours=recent_avg_hours|floatformat:0|intcomma %} - On average, recent imports have taken {{ hours }} hours. - {% endblocktrans %} - {% else %} - {% blocktrans trimmed with minutes=recent_avg_minutes|floatformat:0|intcomma %} - On average, recent imports have taken {{ minutes }} minutes. - {% endblocktrans %} + {% if site.imports_enabled %} + {% if recent_avg_hours or recent_avg_minutes %} +

+

+ {% if recent_avg_hours %} + {% blocktrans trimmed with hours=recent_avg_hours|floatformat:0|intcomma %} + On average, recent imports have taken {{ hours }} hours. + {% endblocktrans %} + {% else %} + {% blocktrans trimmed with minutes=recent_avg_minutes|floatformat:0|intcomma %} + On average, recent imports have taken {{ minutes }} minutes. + {% endblocktrans %} + {% endif %} +

+
{% endif %} + +
+ {% csrf_token %} + +
+
+
+ + +
+ +
+ +

+ {% blocktrans trimmed %} + You can download your Goodreads data from the + Import/Export page + of your Goodreads account. + {% endblocktrans %} +

+
+ +
+ + {{ import_form.csv_file }} +
+
+ +
+
+ +
+
+ + {% include 'snippets/privacy_select.html' with no_label=True privacy_uuid="import" %} +
+
+
+ +
+ {% else %} +
+

+ +

+

+ {% trans "Imports are temporarily disabled; thank you for your patience." %}

{% endif %} - -
- {% csrf_token %} - -
-
-
- - -
- -
- -

- {% blocktrans trimmed %} - You can download your Goodreads data from the - Import/Export page - of your Goodreads account. - {% endblocktrans %} -

-
- -
- - {{ import_form.csv_file }} -
-
- -
-
- -
-
- - {% include 'snippets/privacy_select.html' with no_label=True privacy_uuid="import" %} -
-
-
- -
diff --git a/bookwyrm/views/imports/import_data.py b/bookwyrm/views/imports/import_data.py index f9c62a15d..99fd7fc4c 100644 --- a/bookwyrm/views/imports/import_data.py +++ b/bookwyrm/views/imports/import_data.py @@ -4,6 +4,7 @@ import datetime from django.contrib.auth.decorators import login_required from django.db.models import Avg, ExpressionWrapper, F, fields +from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator from django.http import HttpResponseBadRequest from django.shortcuts import redirect @@ -54,6 +55,10 @@ class Import(View): def post(self, request): """ingest a goodreads csv""" + site = models.Site.objects.get() + if not site.imports_enabled: + raise PermissionDenied() + form = forms.ImportForm(request.POST, request.FILES) if not form.is_valid(): return HttpResponseBadRequest() From 9c5fe7610b26b5fad636a433196c7c243209278c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 17 Nov 2022 15:41:30 -0800 Subject: [PATCH 14/62] Fixes reference to site model --- bookwyrm/views/imports/import_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/views/imports/import_data.py b/bookwyrm/views/imports/import_data.py index 99fd7fc4c..4956bfb7d 100644 --- a/bookwyrm/views/imports/import_data.py +++ b/bookwyrm/views/imports/import_data.py @@ -55,7 +55,7 @@ class Import(View): def post(self, request): """ingest a goodreads csv""" - site = models.Site.objects.get() + site = models.SiteSettings.objects.get() if not site.imports_enabled: raise PermissionDenied() From c5fb710f29095534c905efbedc048768eee20c26 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 18 Nov 2022 08:23:53 -0800 Subject: [PATCH 15/62] Fixes code of conduct link --- bookwyrm/templates/snippets/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/snippets/footer.html b/bookwyrm/templates/snippets/footer.html index eacb6d6b7..77be88bfe 100644 --- a/bookwyrm/templates/snippets/footer.html +++ b/bookwyrm/templates/snippets/footer.html @@ -24,7 +24,7 @@

- {% trans "Code of Conduct" %} + {% trans "Code of Conduct" %}

{% trans "Privacy Policy" %} From c7ac936a9a7fd272d40639001f8a7c10e539066f Mon Sep 17 00:00:00 2001 From: Henry <64515030+henryistaken@users.noreply.github.com> Date: Fri, 18 Nov 2022 23:24:18 -0800 Subject: [PATCH 16/62] Fix Quick Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 558d42d45..f8b2eb1f6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Keep track of what books you've read, and what books you'd like to read in the f Federation allows you to interact with users on other instances and services, and also shares metadata about books and authors, which collaboratively builds a decentralized database of books. ### Privacy and moderation -Users and administrators can control who can see thier posts and what other instances to federate with. +Users and administrators can control who can see their posts and what other instances to federate with. ## Tech Stack Web backend From 238a1b0e8852cdc82bca7ce0f1b5dda8b55bd3ff Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Sat, 19 Nov 2022 17:54:44 +0100 Subject: [PATCH 17/62] set HTTP_X_FORWARDED_PROTO in .env This fixes #2397. The description in .env is whith a warning and a link to the official documentation about what this setting is doing if set to true. --- .env.example | 7 +++++++ bookwyrm/settings.py | 3 +++ 2 files changed, 10 insertions(+) diff --git a/.env.example b/.env.example index 58c53b5bf..319e03eda 100644 --- a/.env.example +++ b/.env.example @@ -108,3 +108,10 @@ OTEL_EXPORTER_OTLP_ENDPOINT= OTEL_EXPORTER_OTLP_HEADERS= # Service name to identify your app OTEL_SERVICE_NAME= + +# Set HTTP_X_FORWARDED_PROTO ONLY to true if you know what you are doing. +# Only use it if your proxy is "swalloing" if the original request was made +# via https. Please refer to the Django-Documentation and assess the risks +# for your instance: +# https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header +HTTP_X_FORWARDED_PROTO=false diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 0fcc00590..6c0b2d176 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -364,3 +364,6 @@ OTEL_EXPORTER_OTLP_HEADERS = env("OTEL_EXPORTER_OTLP_HEADERS", None) OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None) TWO_FACTOR_LOGIN_MAX_SECONDS = 60 + +if HTTP_X_FORWARDED_PROTO: + SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") From 4e2324f06b4522fc5b6d244c22cec6f270533a40 Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Sat, 19 Nov 2022 18:02:34 +0100 Subject: [PATCH 18/62] Add HTTP_X_FORWARDED_PROTO:false to Test that it does not fail any more :) --- .github/workflows/django-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/django-tests.yml b/.github/workflows/django-tests.yml index 97a744813..4335a4605 100644 --- a/.github/workflows/django-tests.yml +++ b/.github/workflows/django-tests.yml @@ -56,5 +56,6 @@ jobs: EMAIL_USE_TLS: true ENABLE_PREVIEW_IMAGES: false ENABLE_THUMBNAIL_GENERATION: true + HTTP_X_FORWARDED_PROTO: false run: | pytest -n 3 From e11811d461f078ea8170a9dcb569d55747aaba96 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 19 Nov 2022 09:48:44 -0800 Subject: [PATCH 19/62] Fancier UI around OTP codes --- bookwyrm/templates/preferences/2fa.html | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templates/preferences/2fa.html b/bookwyrm/templates/preferences/2fa.html index b6bce604f..10de2993f 100644 --- a/bookwyrm/templates/preferences/2fa.html +++ b/bookwyrm/templates/preferences/2fa.html @@ -46,7 +46,29 @@

{{ qrcode | safe }}
-
{{ code | safe }}
+
+ + + {% trans "Use setup key" %} + + + +
+
+ {% trans "Account name:" %} +
+
+ {{ user.username }} +
+ +
+ {% trans "Code:" %} +
+
+ {{ code | safe }} +
+
+
{{ form.otp }} From 6baa58260ab841088c3c721a01fb0319825a28f8 Mon Sep 17 00:00:00 2001 From: Hugh Rundle Date: Sun, 20 Nov 2022 04:51:18 +1100 Subject: [PATCH 20/62] 2fa qrcode fixes (#2407) * 2fa qrcode fixes - add light background to qr code when using dark theme - show OTP secret code under qr code for manual entry on apps if required fixes #2386 fixes #2389 * fix code formatting * revert changes re visible code This conflicts with an existing PR. * i code pretty --- bookwyrm/static/css/themes/bookwyrm-dark.scss | 4 ++++ bookwyrm/templates/preferences/2fa.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bookwyrm/static/css/themes/bookwyrm-dark.scss b/bookwyrm/static/css/themes/bookwyrm-dark.scss index a2eb94efb..b98422688 100644 --- a/bookwyrm/static/css/themes/bookwyrm-dark.scss +++ b/bookwyrm/static/css/themes/bookwyrm-dark.scss @@ -92,6 +92,10 @@ $family-secondary: $family-sans-serif; color: $grey-light !important; } +#qrcode svg { + background-color: #a6a6a6; +} + @import "../bookwyrm.scss"; @import "../vendor/icons.css"; @import "../vendor/shepherd.scss"; diff --git a/bookwyrm/templates/preferences/2fa.html b/bookwyrm/templates/preferences/2fa.html index b0703bc4a..29a011380 100644 --- a/bookwyrm/templates/preferences/2fa.html +++ b/bookwyrm/templates/preferences/2fa.html @@ -45,7 +45,7 @@

{% trans "Scan the QR code with your authentication app and then enter the code from your app below to confirm your app is set up." %}

-
{{ qrcode | safe }}
+
{{ qrcode | safe }}
{{ form.otp }} From 7d51a69c7118badadc126240b611a155f19c86d4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 19 Nov 2022 10:03:40 -0800 Subject: [PATCH 21/62] Expand robots.txt --- bookwyrm/templates/robots.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bookwyrm/templates/robots.txt b/bookwyrm/templates/robots.txt index a328b6e90..4aa091277 100644 --- a/bookwyrm/templates/robots.txt +++ b/bookwyrm/templates/robots.txt @@ -73,6 +73,14 @@ User-agent: PetalBot Disallow: / +User-agent: DataForSeoBot +Disallow: / + +User-agent: YisouSpider +Disallow: / + + User-agent: * +Crawl-delay: 10 Disallow: /static/js/ Disallow: /static/css/ From 31ea868ddcb879d81a9008547ae282de38f777b1 Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Sat, 19 Nov 2022 20:40:36 +0100 Subject: [PATCH 22/62] Update to actual READ the file... --- bookwyrm/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 6c0b2d176..5379a16ff 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -364,6 +364,7 @@ OTEL_EXPORTER_OTLP_HEADERS = env("OTEL_EXPORTER_OTLP_HEADERS", None) OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None) TWO_FACTOR_LOGIN_MAX_SECONDS = 60 - if HTTP_X_FORWARDED_PROTO: + +SECURE_PROXY_SSL_HEADER = env.bool("SECURE_PROXY_SSL_HEADER", false) SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") From 3c1b38ed2c4667ee94b4699fd959d1476f9462dc Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Sun, 20 Nov 2022 17:47:26 +0100 Subject: [PATCH 23/62] one should import what one declares... ... andn not what one thinks was declared^^ --- bookwyrm/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 5379a16ff..45a620847 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -364,7 +364,7 @@ OTEL_EXPORTER_OTLP_HEADERS = env("OTEL_EXPORTER_OTLP_HEADERS", None) OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None) TWO_FACTOR_LOGIN_MAX_SECONDS = 60 -if HTTP_X_FORWARDED_PROTO: -SECURE_PROXY_SSL_HEADER = env.bool("SECURE_PROXY_SSL_HEADER", false) +HTTP_X_FORWARDED_PROTO = env.bool("SECURE_PROXY_SSL_HEADER", false) +if HTTP_X_FORWARDED_PROTO: SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") From 484484fd4b5280c33ffdceb30aaa66f875ad408b Mon Sep 17 00:00:00 2001 From: Jascha Urbach Date: Sun, 20 Nov 2022 17:57:03 +0100 Subject: [PATCH 24/62] Apologies for my lack of coding skills --- .env.example | 2 +- bookwyrm/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 319e03eda..bbd825a9a 100644 --- a/.env.example +++ b/.env.example @@ -110,7 +110,7 @@ OTEL_EXPORTER_OTLP_HEADERS= OTEL_SERVICE_NAME= # Set HTTP_X_FORWARDED_PROTO ONLY to true if you know what you are doing. -# Only use it if your proxy is "swalloing" if the original request was made +# Only use it if your proxy is "swallowing" if the original request was made # via https. Please refer to the Django-Documentation and assess the risks # for your instance: # https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 45a620847..ed0f57839 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -365,6 +365,6 @@ OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None) TWO_FACTOR_LOGIN_MAX_SECONDS = 60 -HTTP_X_FORWARDED_PROTO = env.bool("SECURE_PROXY_SSL_HEADER", false) +HTTP_X_FORWARDED_PROTO = env.bool("SECURE_PROXY_SSL_HEADER", False) if HTTP_X_FORWARDED_PROTO: SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") From cc3cd5c98c72ee1a381df3f48a2b6e4e682bab37 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 20 Nov 2022 19:39:08 -0800 Subject: [PATCH 25/62] Don't use task for password reset emails --- bookwyrm/emailing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/emailing.py b/bookwyrm/emailing.py index 80aacf7f4..03cf4772e 100644 --- a/bookwyrm/emailing.py +++ b/bookwyrm/emailing.py @@ -38,7 +38,7 @@ def password_reset_email(reset_code): data = email_data() data["reset_link"] = reset_code.link data["user"] = reset_code.user.display_name - send_email.delay(reset_code.user.email, *format_email("password_reset", data)) + send_email(reset_code.user.email, *format_email("password_reset", data)) def moderation_report_email(report): From 55bab0b70d4dd0f370b3105b4ec729010ad2183d Mon Sep 17 00:00:00 2001 From: Joel Bradshaw Date: Wed, 23 Nov 2022 19:26:09 +0000 Subject: [PATCH 26/62] Slice queryset before we resolve it This was accidentally querying ALL books in the database to generate suggestions if we didn't have enough, which broke some stuff --- bookwyrm/views/list/list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/views/list/list.py b/bookwyrm/views/list/list.py index 3797997a9..1adf7a679 100644 --- a/bookwyrm/views/list/list.py +++ b/bookwyrm/views/list/list.py @@ -110,8 +110,8 @@ def get_list_suggestions(book_list, user, query=None): s.default_edition for s in models.Work.objects.filter( ~Q(editions__in=book_list.books.all()), - ).order_by("-updated_date") - ][: 5 - len(suggestions)] + ).order_by("-updated_date")[: 5 - len(suggestions)] + ] return suggestions From dd83e32f32a141c4484861aaf2f82a45b30c474c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 23 Nov 2022 22:00:34 -0800 Subject: [PATCH 27/62] Fixes unit test --- bookwyrm/tests/test_emailing.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/test_emailing.py b/bookwyrm/tests/test_emailing.py index ecfbd9448..b2af59f4f 100644 --- a/bookwyrm/tests/test_emailing.py +++ b/bookwyrm/tests/test_emailing.py @@ -11,6 +11,7 @@ from bookwyrm import emailing, models class Emailing(TestCase): """every response to a get request, html or json""" + # pylint: disable=invalid-name def setUp(self): """we need basic test data and mocks""" self.factory = RequestFactory() @@ -41,10 +42,12 @@ class Emailing(TestCase): self.assertEqual(args[1], "You're invited to join BookWyrm!") self.assertEqual(len(args), 4) - def test_password_reset_email(self, email_mock): + def test_password_reset_email(self, _): """load the password reset email""" reset = models.PasswordReset.objects.create(user=self.local_user) - emailing.password_reset_email(reset) + + with patch("bookwyrm.emailing.send_email") as email_mock: + emailing.password_reset_email(reset) self.assertEqual(email_mock.call_count, 1) args = email_mock.call_args[0] From 81ab08aaa3e0469d7a72c23d098de5800bf5e860 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 08:28:45 -0800 Subject: [PATCH 28/62] Fixes editing links --- bookwyrm/forms/links.py | 21 ++++++------ .../templates/book/file_links/edit_links.html | 1 + bookwyrm/tests/views/books/test_links.py | 1 + bookwyrm/views/books/links.py | 32 +++++++++++++++---- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/bookwyrm/forms/links.py b/bookwyrm/forms/links.py index de229bc2d..d2fd5f116 100644 --- a/bookwyrm/forms/links.py +++ b/bookwyrm/forms/links.py @@ -36,13 +36,16 @@ class FileLinkForm(CustomForm): "This domain is blocked. Please contact your administrator if you think this is an error." ), ) - elif models.FileLink.objects.filter( + if ( + not self.instance + and models.FileLink.objects.filter( url=url, book=book, filetype=filetype - ).exists(): - # pylint: disable=line-too-long - self.add_error( - "url", - _( - "This link with file type has already been added for this book. If it is not visible, the domain is still pending." - ), - ) + ).exists() + ): + # pylint: disable=line-too-long + self.add_error( + "url", + _( + "This link with file type has already been added for this book. If it is not visible, the domain is still pending." + ), + ) diff --git a/bookwyrm/templates/book/file_links/edit_links.html b/bookwyrm/templates/book/file_links/edit_links.html index 77431726b..e6819f6bb 100644 --- a/bookwyrm/templates/book/file_links/edit_links.html +++ b/bookwyrm/templates/book/file_links/edit_links.html @@ -86,6 +86,7 @@
+ {% include 'snippets/form_errors.html' with errors_list=link.form.availability.errors id="desc_availability" %} diff --git a/bookwyrm/tests/views/books/test_links.py b/bookwyrm/tests/views/books/test_links.py index 3379786b0..bace38b7e 100644 --- a/bookwyrm/tests/views/books/test_links.py +++ b/bookwyrm/tests/views/books/test_links.py @@ -15,6 +15,7 @@ from bookwyrm.tests.validate_html import validate_html class LinkViews(TestCase): """books books books""" + # pylint: disable=invalid-name def setUp(self): """we need basic test data and mocks""" self.factory = RequestFactory() diff --git a/bookwyrm/views/books/links.py b/bookwyrm/views/books/links.py index 60153f8c3..70b91f2d9 100644 --- a/bookwyrm/views/books/links.py +++ b/bookwyrm/views/books/links.py @@ -21,11 +21,7 @@ class BookFileLinks(View): def get(self, request, book_id): """view links""" book = get_object_or_404(models.Edition, id=book_id) - 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) + annotated_links = get_annotated_links(book) data = {"book": book, "links": annotated_links} return TemplateResponse(request, "book/file_links/edit_links.html", data) @@ -34,8 +30,30 @@ class BookFileLinks(View): """Edit a link""" link = get_object_or_404(models.FileLink, id=link_id, book=book_id) form = forms.FileLinkForm(request.POST, instance=link) - form.save(request) - return self.get(request, book_id) + if form.is_valid(): + form.save(request) + return redirect("file-link", book_id) + + # this form shouldn't ever really get here, since it's just a dropdown + # get the data again rather than redirecting + book = get_object_or_404(models.Edition, id=book_id) + annotated_links = get_annotated_links(book, form=form) + + data = {"book": book, "links": annotated_links} + return TemplateResponse(request, "book/file_links/edit_links.html", data) + + +def get_annotated_links(book, form=None): + """The links for this book, plus the forms to edit those links""" + links = book.file_links.order_by("domain__status", "created_date") + annotated_links = [] + for link in links.all(): + if form and link.id == form.instance.id: + link.form = form + else: + link.form = forms.FileLinkForm(instance=link) + annotated_links.append(link) + return annotated_links @require_POST From be33fe60408c583dff60c27dda5618ad0e0a2174 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 08:32:10 -0800 Subject: [PATCH 29/62] Updates locales --- locale/ca_ES/LC_MESSAGES/django.mo | Bin 130365 -> 137000 bytes locale/ca_ES/LC_MESSAGES/django.po | 215 ++++++++++------- locale/de_DE/LC_MESSAGES/django.mo | Bin 130931 -> 133604 bytes locale/de_DE/LC_MESSAGES/django.po | 101 +++++--- locale/en_US/LC_MESSAGES/django.po | 244 +++++++++++-------- locale/es_ES/LC_MESSAGES/django.mo | Bin 127829 -> 134686 bytes locale/es_ES/LC_MESSAGES/django.po | 189 +++++++++------ locale/fi_FI/LC_MESSAGES/django.mo | Bin 132958 -> 132958 bytes locale/fi_FI/LC_MESSAGES/django.po | 83 ++++--- locale/fr_FR/LC_MESSAGES/django.mo | Bin 138871 -> 140432 bytes locale/fr_FR/LC_MESSAGES/django.po | 113 +++++---- locale/gl_ES/LC_MESSAGES/django.mo | Bin 103908 -> 133681 bytes locale/gl_ES/LC_MESSAGES/django.po | 349 +++++++++++++++------------ locale/it_IT/LC_MESSAGES/django.mo | Bin 130107 -> 133211 bytes locale/it_IT/LC_MESSAGES/django.po | 141 ++++++----- locale/lt_LT/LC_MESSAGES/django.mo | Bin 101460 -> 101460 bytes locale/lt_LT/LC_MESSAGES/django.po | 83 ++++--- locale/no_NO/LC_MESSAGES/django.mo | Bin 76049 -> 76049 bytes locale/no_NO/LC_MESSAGES/django.po | 83 ++++--- locale/pl_PL/LC_MESSAGES/django.mo | Bin 123178 -> 123178 bytes locale/pl_PL/LC_MESSAGES/django.po | 83 ++++--- locale/pt_BR/LC_MESSAGES/django.mo | Bin 85224 -> 85840 bytes locale/pt_BR/LC_MESSAGES/django.po | 99 +++++--- locale/pt_PT/LC_MESSAGES/django.mo | Bin 79333 -> 79333 bytes locale/pt_PT/LC_MESSAGES/django.po | 83 ++++--- locale/ro_RO/LC_MESSAGES/django.mo | Bin 125085 -> 125085 bytes locale/ro_RO/LC_MESSAGES/django.po | 83 ++++--- locale/sv_SE/LC_MESSAGES/django.mo | Bin 85241 -> 85241 bytes locale/sv_SE/LC_MESSAGES/django.po | 83 ++++--- locale/zh_Hans/LC_MESSAGES/django.mo | Bin 79851 -> 79851 bytes locale/zh_Hans/LC_MESSAGES/django.po | 83 ++++--- locale/zh_Hant/LC_MESSAGES/django.mo | Bin 32683 -> 32683 bytes locale/zh_Hant/LC_MESSAGES/django.po | 83 ++++--- 33 files changed, 1350 insertions(+), 848 deletions(-) diff --git a/locale/ca_ES/LC_MESSAGES/django.mo b/locale/ca_ES/LC_MESSAGES/django.mo index 4d51492b04d3dfd8d69d4ce7eb86f3ec56839580..9576df387610981d907e1e7bfefc75f142e01f1b 100644 GIT binary patch delta 33916 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

wECDB7;2dr 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 J-ZLul0RW@J8HfM? delta 27581 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

diff --git a/bookwyrm/templatetags/book_display_tags.py b/bookwyrm/templatetags/book_display_tags.py index 049a2b256..eba09c894 100644 --- a/bookwyrm/templatetags/book_display_tags.py +++ b/bookwyrm/templatetags/book_display_tags.py @@ -1,10 +1,16 @@ """ template filters """ from django import template +from bookwyrm import models register = template.Library() +@register.filter(name="review_count") +def get_review_count(book): + """how many reviews?""" + return models.Review.objects.filter(deleted=False, book=book).count() + @register.filter(name="book_description") def get_book_description(book): """use the work's text if the book doesn't have it""" From 015af2c1e7f41d872e779b0d2c93e592f1aa7adb Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 10:35:16 -0800 Subject: [PATCH 34/62] Don't start imports with empty csvs Fixes #2353 --- bookwyrm/importers/importer.py | 3 +++ bookwyrm/templates/import/import.html | 6 ++++++ bookwyrm/views/imports/import_data.py | 6 +++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index a3cfba198..388dcf91f 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -37,6 +37,9 @@ class Importer: """check over a csv and creates a database entry for the job""" csv_reader = csv.DictReader(csv_file, delimiter=self.delimiter) rows = enumerate(list(csv_reader)) + if rows: + raise ValueError("CSV file is empty") + job = ImportJob.objects.create( user=user, include_reviews=include_reviews, diff --git a/bookwyrm/templates/import/import.html b/bookwyrm/templates/import/import.html index 141e5671e..325caa92b 100644 --- a/bookwyrm/templates/import/import.html +++ b/bookwyrm/templates/import/import.html @@ -8,6 +8,12 @@

{% trans "Import Books" %}

+ {% if invalid %} +
+ {% trans "Not a valid CSV file" %} +
+ {% endif %} + {% if site.imports_enabled %} {% if recent_avg_hours or recent_avg_minutes %}
diff --git a/bookwyrm/views/imports/import_data.py b/bookwyrm/views/imports/import_data.py index 4956bfb7d..f0bb2e698 100644 --- a/bookwyrm/views/imports/import_data.py +++ b/bookwyrm/views/imports/import_data.py @@ -11,7 +11,6 @@ from django.shortcuts import redirect from django.template.response import TemplateResponse from django.utils import timezone from django.utils.decorators import method_decorator -from django.utils.translation import gettext_lazy as _ from django.views import View from bookwyrm import forms, models @@ -30,7 +29,7 @@ from bookwyrm.utils.cache import get_or_set class Import(View): """import view""" - def get(self, request): + def get(self, request, invalid=False): """load import page""" jobs = models.ImportJob.objects.filter(user=request.user).order_by( "-created_date" @@ -43,6 +42,7 @@ class Import(View): "page_range": paginated.get_elided_page_range( page.number, on_each_side=2, on_ends=1 ), + "invalid": invalid, } seconds = get_or_set("avg-import-time", get_average_import_time, timeout=86400) @@ -88,7 +88,7 @@ class Import(View): privacy, ) except (UnicodeDecodeError, ValueError, KeyError): - return HttpResponseBadRequest(_("Not a valid csv file")) + return self.get(request, invalid=True) job.start_job() From e94e72a5945abb753e686d786e304853db819b4a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 10:40:06 -0800 Subject: [PATCH 35/62] Python formatting --- bookwyrm/templatetags/book_display_tags.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bookwyrm/templatetags/book_display_tags.py b/bookwyrm/templatetags/book_display_tags.py index eba09c894..56eb096ec 100644 --- a/bookwyrm/templatetags/book_display_tags.py +++ b/bookwyrm/templatetags/book_display_tags.py @@ -11,6 +11,7 @@ def get_review_count(book): """how many reviews?""" return models.Review.objects.filter(deleted=False, book=book).count() + @register.filter(name="book_description") def get_book_description(book): """use the work's text if the book doesn't have it""" From 0a12be82796526a82d0c55fdaa1e9926d0e006c5 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 10:41:04 -0800 Subject: [PATCH 36/62] Appease pylint --- bookwyrm/connectors/inventaire.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/connectors/inventaire.py b/bookwyrm/connectors/inventaire.py index b83295df9..a330b2c4a 100644 --- a/bookwyrm/connectors/inventaire.py +++ b/bookwyrm/connectors/inventaire.py @@ -165,7 +165,7 @@ class Connector(AbstractConnector): edition_data = self.get_book_data(edition_data) except ConnectorException: # who, indeed, knows - return + return None return super().create_edition_from_data(work, edition_data, instance=instance) def get_cover_url(self, cover_blob, *_): From 304757091eca4147ec662b76a04a2688108f3e8a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 10:53:42 -0800 Subject: [PATCH 37/62] Fixes checking csv length to consider headers --- bookwyrm/importers/importer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index 388dcf91f..e4b19f20a 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -36,9 +36,10 @@ class Importer: def create_job(self, user, csv_file, include_reviews, privacy): """check over a csv and creates a database entry for the job""" csv_reader = csv.DictReader(csv_file, delimiter=self.delimiter) - rows = enumerate(list(csv_reader)) - if rows: + rows = list(csv_reader) + if len(rows) <= 1: raise ValueError("CSV file is empty") + rows = enumerate(rows) job = ImportJob.objects.create( user=user, From 16f9232e1e75400154fae15fde24c9cac94667fa Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 11:02:42 -0800 Subject: [PATCH 38/62] Adds database fields for legal page/impressum --- .../migrations/0167_auto_20221125_1900.py | 23 +++++++++++++++++++ bookwyrm/models/site.py | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 bookwyrm/migrations/0167_auto_20221125_1900.py diff --git a/bookwyrm/migrations/0167_auto_20221125_1900.py b/bookwyrm/migrations/0167_auto_20221125_1900.py new file mode 100644 index 000000000..db258b7c5 --- /dev/null +++ b/bookwyrm/migrations/0167_auto_20221125_1900.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.16 on 2022-11-25 19:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0166_sitesettings_imports_enabled"), + ] + + operations = [ + migrations.AddField( + model_name="sitesettings", + name="impressum", + field=models.TextField(default="Add a impressum here."), + ), + migrations.AddField( + model_name="sitesettings", + name="show_impressum", + field=models.BooleanField(default=False), + ), + ] diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index 9e97ede9a..533a37b30 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -62,6 +62,8 @@ class SiteSettings(SiteModel): ) code_of_conduct = models.TextField(default="Add a code of conduct here.") privacy_policy = models.TextField(default="Add a privacy policy here.") + impressum = models.TextField(default="Add a impressum here.") + show_impressum = models.BooleanField(default=False) # registration allow_registration = models.BooleanField(default=False) From 70d639440e2b14f6c4ed4910826924066bace21e Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 12:06:32 -0800 Subject: [PATCH 39/62] Show impressum --- bookwyrm/templates/about/layout.html | 8 ++++++++ bookwyrm/templates/settings/site.html | 13 +++++++++++++ bookwyrm/templates/snippets/footer.html | 5 +++++ bookwyrm/urls.py | 1 + bookwyrm/views/__init__.py | 2 +- bookwyrm/views/landing/about.py | 10 ++++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templates/about/layout.html b/bookwyrm/templates/about/layout.html index e921fcd29..22237508c 100644 --- a/bookwyrm/templates/about/layout.html +++ b/bookwyrm/templates/about/layout.html @@ -47,6 +47,14 @@ {% trans "Privacy Policy" %} + {% if site.show_impressum %} +
  • + {% url 'impressum' as path %} + + {% trans "Impressum" %} + +
  • + {% endif %} diff --git a/bookwyrm/templates/settings/site.html b/bookwyrm/templates/settings/site.html index 4fd147834..4cfa531e5 100644 --- a/bookwyrm/templates/settings/site.html +++ b/bookwyrm/templates/settings/site.html @@ -68,6 +68,19 @@ {{ site_form.privacy_policy }}
    + +
    + + {{ site_form.impressum }} +
    +
    +
    + +
    +
    + {{ site_form.show_impressum }} +
    +
    diff --git a/bookwyrm/templates/snippets/footer.html b/bookwyrm/templates/snippets/footer.html index 77be88bfe..a1f13a5b8 100644 --- a/bookwyrm/templates/snippets/footer.html +++ b/bookwyrm/templates/snippets/footer.html @@ -29,6 +29,11 @@

    {% trans "Privacy Policy" %}

    + {% if site.show_impressum %} +

    + {% trans "Impressum" %} +

    + {% endif %}
    {% if site.support_link %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index daf05e10e..90d0d0edc 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -318,6 +318,7 @@ urlpatterns = [ re_path(r"^about/?$", views.about, name="about"), re_path(r"^privacy/?$", views.privacy, name="privacy"), re_path(r"^conduct/?$", views.conduct, name="conduct"), + re_path(r"^impressum/?$", views.impressum, name="impressum"), path("", views.Home.as_view(), name="landing"), re_path(r"^discover/?$", views.Discover.as_view(), name="discover"), re_path(r"^notifications/?$", views.Notifications.as_view(), name="notifications"), diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 21e33450c..bc70490c5 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -60,7 +60,7 @@ from .books.editions import Editions, switch_edition from .books.links import BookFileLinks, AddFileLink, delete_link # landing -from .landing.about import about, privacy, conduct +from .landing.about import about, privacy, conduct, impressum from .landing.landing import Home, Landing from .landing.login import Login, Logout from .landing.register import Register diff --git a/bookwyrm/views/landing/about.py b/bookwyrm/views/landing/about.py index 7016dfcdb..0bcdfd836 100644 --- a/bookwyrm/views/landing/about.py +++ b/bookwyrm/views/landing/about.py @@ -1,5 +1,6 @@ """ non-interactive pages """ from dateutil.relativedelta import relativedelta +from django.http import Http404 from django.template.response import TemplateResponse from django.utils import timezone from django.views.decorators.http import require_GET @@ -36,3 +37,12 @@ def conduct(request): def privacy(request): """more information about the instance""" return TemplateResponse(request, "about/privacy.html") + + +@require_GET +def impressum(request): + """more information about the instance""" + site = models.SiteSettings.objects.get() + if not site.show_impressum: + raise Http404() + return TemplateResponse(request, "about/impressum.html") From 8b068e94d5d4242e3e5e25909bd6fbe54666935c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 12:19:49 -0800 Subject: [PATCH 40/62] Headers aren't one of the rows --- bookwyrm/importers/importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index e4b19f20a..a2641ff11 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -37,7 +37,7 @@ class Importer: """check over a csv and creates a database entry for the job""" csv_reader = csv.DictReader(csv_file, delimiter=self.delimiter) rows = list(csv_reader) - if len(rows) <= 1: + if len(rows) < 1: raise ValueError("CSV file is empty") rows = enumerate(rows) From 8ec984c3ff9771430540fbfed48f18809d888c2c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 25 Nov 2022 14:37:27 -0800 Subject: [PATCH 41/62] Tick version number --- bookwyrm/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index ed0f57839..1a3238a1f 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _ env = Env() env.read_env() DOMAIN = env("DOMAIN") -VERSION = "0.5.1" +VERSION = "0.5.2" RELEASE_API = env( "RELEASE_API", From cac9dc2603026444b18145940074d43c506667e1 Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 11:33:16 -0800 Subject: [PATCH 42/62] registration answer was not long enough client side, fixes #2377 for real --- bookwyrm/templates/landing/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/landing/layout.html b/bookwyrm/templates/landing/layout.html index bf0a6b2a1..46cfe49ea 100644 --- a/bookwyrm/templates/landing/layout.html +++ b/bookwyrm/templates/landing/layout.html @@ -73,7 +73,7 @@ {% if site.invite_request_question %}
    - + {% include 'snippets/form_errors.html' with errors_list=request_form.answer.errors id="desc_answer_register" %}
    {% endif %} From e0896e3828a9ff4d1d5e769c1bbd2bc1fae7c3c5 Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 11:54:55 -0800 Subject: [PATCH 43/62] fix input type --- bookwyrm/templates/landing/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/landing/layout.html b/bookwyrm/templates/landing/layout.html index 46cfe49ea..e3cdf1bdf 100644 --- a/bookwyrm/templates/landing/layout.html +++ b/bookwyrm/templates/landing/layout.html @@ -73,7 +73,7 @@ {% if site.invite_request_question %}
    - + {% include 'snippets/form_errors.html' with errors_list=request_form.answer.errors id="desc_answer_register" %}
    {% endif %} From c4398ff18747752ee26ed9bc66ad094fbaf7c83b Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 12:14:48 -0800 Subject: [PATCH 44/62] make use of templates instead of hidden divs which is more elegant --- bookwyrm/static/js/bookwyrm.js | 5 +- bookwyrm/templates/search/barcode_modal.html | 94 ++++++++++---------- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/bookwyrm/static/js/bookwyrm.js b/bookwyrm/static/js/bookwyrm.js index aa06a8b0a..eddbfbee3 100644 --- a/bookwyrm/static/js/bookwyrm.js +++ b/bookwyrm/static/js/bookwyrm.js @@ -628,9 +628,8 @@ let BookWyrm = new (class { } function toggleStatus(status) { - for (const child of statusNode.children) { - BookWyrm.toggleContainer(child, !child.classList.contains(status)); - } + const template = document.querySelector(`#barcode-${status}`); + statusNode.replaceChildren(template ? template.content.cloneNode(true) : null); } function initBarcodes(cameraId = null) { diff --git a/bookwyrm/templates/search/barcode_modal.html b/bookwyrm/templates/search/barcode_modal.html index 70481b20a..519adfd3b 100644 --- a/bookwyrm/templates/search/barcode_modal.html +++ b/bookwyrm/templates/search/barcode_modal.html @@ -1,48 +1,46 @@ -{% extends 'components/modal.html' %} -{% load i18n %} - -{% block modal-title %} - {% blocktrans %} - Scan Barcode - {% endblocktrans %} -{% endblock %} - -{% block modal-body %} -
    -
    -
    - -
    - -
    - -
    - - - - -
    -{% endblock %} - -{% block modal-footer %} - -{% endblock %} - - +{% extends 'components/modal.html' %} +{% load i18n %} + +{% block modal-title %} + {% blocktrans %} + Scan Barcode + {% endblocktrans %} +{% endblock %} + +{% block modal-body %} +
    +
    +
    + +
    + +
    + + + + +
    +{% endblock %} + +{% block modal-footer %} + +{% endblock %} + + From c2b447cf49f4e55f448c80667ef6332cbf580bab Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 12:19:37 -0800 Subject: [PATCH 45/62] lint --- bookwyrm/static/js/bookwyrm.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bookwyrm/static/js/bookwyrm.js b/bookwyrm/static/js/bookwyrm.js index eddbfbee3..5b3f13d4a 100644 --- a/bookwyrm/static/js/bookwyrm.js +++ b/bookwyrm/static/js/bookwyrm.js @@ -629,6 +629,7 @@ let BookWyrm = new (class { function toggleStatus(status) { const template = document.querySelector(`#barcode-${status}`); + statusNode.replaceChildren(template ? template.content.cloneNode(true) : null); } From d9ecd35024275064c944405ba0e518baef071f99 Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 12:36:52 -0800 Subject: [PATCH 46/62] Remove no-longer present `compilescss` from fish autocomplete config --- complete_bwdev.fish | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/complete_bwdev.fish b/complete_bwdev.fish index fbdae2492..208d7ee4f 100644 --- a/complete_bwdev.fish +++ b/complete_bwdev.fish @@ -1,5 +1,5 @@ # bw-dev auto-completions for fish-shell. -# copy this to ~./.config/fish/completions/ with the name `bw-dev.fish` +# copy this to ~/.config/fish/completions/ with the name `bw-dev.fish` # this will only work if renamed to `bw-dev.fish`. set -l commands up \ @@ -61,7 +61,6 @@ __bw_complete "$commands" "black" "run Python code formatting __bw_complete "$commands" "prettier" "run JavaScript code formatting tool" __bw_complete "$commands" "stylelint" "run SCSS linting tool" __bw_complete "$commands" "formatters" "run multiple formatter tools" -__bw_complete "$commands" "compilescss" "compile the SCSS layouts to CSS" __bw_complete "$commands" "populate_streams" "populate the main streams" __bw_complete "$commands" "populate_lists_streams" "populate streams for book lists" __bw_complete "$commands" "populate_suggestions" "populate book suggestions" From 86d1c8552e0162371009aceab549a9626077ec39 Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 13:17:48 -0800 Subject: [PATCH 47/62] use transparent buttons for reply and more --- bookwyrm/templates/snippets/status/layout.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/snippets/status/layout.html b/bookwyrm/templates/snippets/status/layout.html index 3dd576fee..4e5b75cc0 100644 --- a/bookwyrm/templates/snippets/status/layout.html +++ b/bookwyrm/templates/snippets/status/layout.html @@ -32,7 +32,7 @@ {% if not moderation_mode %} {% endif %} From 6cf028994e4fdebe97bc2c492333e5726abe99cc Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 13:18:11 -0800 Subject: [PATCH 48/62] dark theme: increase background color darkness, fix shepherd color --- bookwyrm/static/css/themes/bookwyrm-dark.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bookwyrm/static/css/themes/bookwyrm-dark.scss b/bookwyrm/static/css/themes/bookwyrm-dark.scss index b98422688..ae904b4a4 100644 --- a/bookwyrm/static/css/themes/bookwyrm-dark.scss +++ b/bookwyrm/static/css/themes/bookwyrm-dark.scss @@ -15,6 +15,8 @@ $danger: #872538; $danger-light: #481922; $light: #393939; $red: #ffa1b4; +$black: #000; +$white-ter: hsl(0, 0%, 90%); /* book cover standins */ $no-cover-color: #002549; @@ -56,9 +58,12 @@ $link-active: $white-bis; $link-light: #0d1c26; /* bulma overrides */ +$body-background-color: rgb(17, 18, 18); $background: $background-secondary; $menu-item-active-background-color: $link-background; $navbar-dropdown-item-hover-color: $white; +$info-light: $background-body; +$info-dark: #72b6ee; /* These element's colors are hardcoded, probably a bug in bulma? */ @media screen and (min-width: 769px) { @@ -74,7 +79,7 @@ $navbar-dropdown-item-hover-color: $white; } /* misc */ -$shadow: 0 0.5em 1em -0.125em rgba($black, 0.2), 0 0px 0 1px rgba($black, 0.02); +$shadow: 0 0.5em 0.5em -0.125em rgba($black, 0.2), 0 0px 0 1px rgba($black, 0.02); $card-header-shadow: 0 0.125em 0.25em rgba($black, 0.1); $invisible-overlay-background-color: rgba($black, 0.66); $progress-value-background-color: $border-light; @@ -92,6 +97,7 @@ $family-secondary: $family-sans-serif; color: $grey-light !important; } + #qrcode svg { background-color: #a6a6a6; } From 9738c9617563495c7a6a18cd3c94899ca723c9a3 Mon Sep 17 00:00:00 2001 From: Vivianne Langdon Date: Sat, 26 Nov 2022 13:26:11 -0800 Subject: [PATCH 49/62] fix css which caused weird spacing for desc. field --- bookwyrm/templates/book/book.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/book/book.html b/bookwyrm/templates/book/book.html index 95829ae9d..4e7862f7a 100644 --- a/bookwyrm/templates/book/book.html +++ b/bookwyrm/templates/book/book.html @@ -194,10 +194,10 @@