From 6b3447761f2f27c1c2d09a545fff1edbb6421ec4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Thu, 20 May 2021 18:16:35 -0700 Subject: [PATCH 001/178] Adds book format field with choices --- ...l_format_edition_physical_format_detail.py | 35 +++++++++++++++++++ bookwyrm/models/book.py | 12 ++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py diff --git a/bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py b/bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py new file mode 100644 index 00000000..05ad1a2b --- /dev/null +++ b/bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2 on 2021-05-21 00:17 + +from django.db import migrations +import bookwyrm + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0075_announcement"), + ] + + operations = [ + migrations.RenameField( + model_name="edition", + old_name="physical_format", + new_name="physical_format_detail", + ), + migrations.AddField( + model_name="edition", + name="physical_format", + field=bookwyrm.models.fields.CharField( + blank=True, + choices=[ + ("AudiobookFormat", "Audiobookformat"), + ("EBook", "Ebook"), + ("GraphicNovel", "Graphicnovel"), + ("Hardcover", "Hardcover"), + ("Paperback", "Paperback"), + ], + max_length=255, + null=True, + ), + ), + ] diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 869ff04d..ae79223d 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -169,6 +169,13 @@ class Work(OrderedCollectionPageMixin, Book): deserialize_reverse_fields = [("editions", "editions")] +# https://schema.org/BookFormatType +FormatChoices = models.TextChoices( + "FormatChoices", + "AudiobookFormat EBook GraphicNovel Hardcover Paperback", +) + + class Edition(Book): """an edition of a book""" @@ -186,7 +193,10 @@ class Edition(Book): max_length=255, blank=True, null=True, deduplication_field=True ) pages = fields.IntegerField(blank=True, null=True) - physical_format = fields.CharField(max_length=255, blank=True, null=True) + physical_format = fields.CharField( + max_length=255, choices=FormatChoices.choices, null=True, blank=True + ) + physical_format_detail = fields.CharField(max_length=255, blank=True, null=True) publishers = fields.ArrayField( models.CharField(max_length=255), blank=True, default=list ) From e99e4831f13cf01cec8b418473da1c3d7ee7d695 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 19 Jun 2021 08:17:27 -0700 Subject: [PATCH 002/178] Updates migration --- ...l_format_edition_physical_format_detail.py | 35 ------ ...l_format_edition_physical_format_detail.py | 100 ++++++++++++++++++ 2 files changed, 100 insertions(+), 35 deletions(-) delete mode 100644 bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py create mode 100644 bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py diff --git a/bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py b/bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py deleted file mode 100644 index 05ad1a2b..00000000 --- a/bookwyrm/migrations/0076_rename_physical_format_edition_physical_format_detail.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2 on 2021-05-21 00:17 - -from django.db import migrations -import bookwyrm - - -class Migration(migrations.Migration): - - dependencies = [ - ("bookwyrm", "0075_announcement"), - ] - - operations = [ - migrations.RenameField( - model_name="edition", - old_name="physical_format", - new_name="physical_format_detail", - ), - migrations.AddField( - model_name="edition", - name="physical_format", - field=bookwyrm.models.fields.CharField( - blank=True, - choices=[ - ("AudiobookFormat", "Audiobookformat"), - ("EBook", "Ebook"), - ("GraphicNovel", "Graphicnovel"), - ("Hardcover", "Hardcover"), - ("Paperback", "Paperback"), - ], - max_length=255, - null=True, - ), - ), - ] diff --git a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py new file mode 100644 index 00000000..ab152252 --- /dev/null +++ b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py @@ -0,0 +1,100 @@ +# Generated by Django 3.2 on 2021-05-21 00:17 + +from django.db import migrations +import bookwyrm + + +def infer_format(app_registry, schema_editor): + """set the new phsyical format field based on existing format data""" + db_alias = schema_editor.connection.alias + + editions = ( + app_registry.get_model("bookwyrm", "Edition") + .objects.using(db_alias) + .filter(physical_format__isnull=False) + ) + mappings = { + "paperback": "Paperback", + "soft": "Paperback", + "pamphlet": "Paperback", + "peperback": "Paperback", + "tapa blanda": "Paperback", + "turtleback": "Paperback", + "pocket": "Paperback", + "spiral": "Paperback", + "ring": "Paperback", + "平装": "Paperback", + "简装": "Paperback", + "hardcover": "Hardcover", + "hardcocer": "Hardcover", + "hardover": "Hardcover", + "hardback": "Hardcover", + "library": "Hardcover", + "tapa dura": "Hardcover", + "leather": "Hardcover", + "clothbound": "Hardcover", + "精装": "Hardcover", + "ebook": "EBook", + "e-book": "EBook", + "digital": "EBook", + "computer file": "EBook", + "epub": "EBook", + "online": "EBook", + "pdf": "EBook", + "elektronische": "EBook", + "electronic": "EBook", + "audiobook": "AudiobookFormat", + "audio": "AudiobookFormat", + "cd": "AudiobookFormat", + "dvd": "AudiobookFormat", + "mp3": "AudiobookFormat", + "cassette": "AudiobookFormat", + "kindle": "AudiobookFormat", + "talking": "AudiobookFormat", + "sound": "AudiobookFormat", + "comic": "GraphicNovel", + "graphic": "GraphicNovel", + } + for edition in editions: + free_format = editions.physical_format_detail.lower() + if free_format in mappings: + edition.physical_format = mappings[free_format] + edition.save(broadcast=False) + else: + matches = [v for k, v in mappings if k in free_format] + if not matches: + continue + edition.physical_format = matches[0] + edition.save(broadcast=False) + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0076_preview_images"), + ] + + operations = [ + migrations.RenameField( + model_name="edition", + old_name="physical_format", + new_name="physical_format_detail", + ), + migrations.AddField( + model_name="edition", + name="physical_format", + field=bookwyrm.models.fields.CharField( + blank=True, + choices=[ + ("AudiobookFormat", "Audiobookformat"), + ("EBook", "Ebook"), + ("GraphicNovel", "Graphicnovel"), + ("Hardcover", "Hardcover"), + ("Paperback", "Paperback"), + ], + max_length=255, + null=True, + ), + ), + migrations.RunPython(infer_format), + ] From cb811c92a668e2e6766542edebf4565dd084c952 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 19 Jun 2021 08:36:12 -0700 Subject: [PATCH 003/178] Adds reverse migration stub --- ...ename_physical_format_edition_physical_format_detail.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py index ab152252..eec94d62 100644 --- a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py +++ b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py @@ -57,16 +57,21 @@ def infer_format(app_registry, schema_editor): } for edition in editions: free_format = editions.physical_format_detail.lower() + print(free_format) if free_format in mappings: + print("hi") edition.physical_format = mappings[free_format] edition.save(broadcast=False) else: matches = [v for k, v in mappings if k in free_format] + print(matches) if not matches: continue edition.physical_format = matches[0] edition.save(broadcast=False) +def reverse(app_registry, schema_editor): + """doesn't need to do anything""" class Migration(migrations.Migration): @@ -96,5 +101,5 @@ class Migration(migrations.Migration): null=True, ), ), - migrations.RunPython(infer_format), + migrations.RunPython(infer_format, reverse), ] From f82161d648cb06f57cabc57f45f13ac99f4f43fd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 19 Jun 2021 08:37:53 -0700 Subject: [PATCH 004/178] Fixes migration query --- ...hysical_format_edition_physical_format_detail.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py index eec94d62..020cc49a 100644 --- a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py +++ b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py @@ -11,7 +11,7 @@ def infer_format(app_registry, schema_editor): editions = ( app_registry.get_model("bookwyrm", "Edition") .objects.using(db_alias) - .filter(physical_format__isnull=False) + .filter(physical_format_detail__isnull=False) ) mappings = { "paperback": "Paperback", @@ -56,19 +56,16 @@ def infer_format(app_registry, schema_editor): "graphic": "GraphicNovel", } for edition in editions: - free_format = editions.physical_format_detail.lower() - print(free_format) + free_format = edition.physical_format_detail.lower() if free_format in mappings: - print("hi") edition.physical_format = mappings[free_format] - edition.save(broadcast=False) + edition.save() else: - matches = [v for k, v in mappings if k in free_format] - print(matches) + matches = [v for k, v in mappings.items() if k in free_format] if not matches: continue edition.physical_format = matches[0] - edition.save(broadcast=False) + edition.save() def reverse(app_registry, schema_editor): """doesn't need to do anything""" From 48fcdcbe93a6171ef249e22726ad4ae0e56ea80c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 4 Aug 2021 14:12:37 -0700 Subject: [PATCH 005/178] Python formatting --- ...077_rename_physical_format_edition_physical_format_detail.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py index 020cc49a..fcff9352 100644 --- a/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py +++ b/bookwyrm/migrations/0077_rename_physical_format_edition_physical_format_detail.py @@ -67,9 +67,11 @@ def infer_format(app_registry, schema_editor): edition.physical_format = matches[0] edition.save() + def reverse(app_registry, schema_editor): """doesn't need to do anything""" + class Migration(migrations.Migration): dependencies = [ From 2c08be79f86f11f103d09645eebe02b8d10c6111 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 4 Aug 2021 14:19:17 -0700 Subject: [PATCH 006/178] Merge migration --- bookwyrm/migrations/0080_merge_20210804_2114.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 bookwyrm/migrations/0080_merge_20210804_2114.py diff --git a/bookwyrm/migrations/0080_merge_20210804_2114.py b/bookwyrm/migrations/0080_merge_20210804_2114.py new file mode 100644 index 00000000..d8365eaf --- /dev/null +++ b/bookwyrm/migrations/0080_merge_20210804_2114.py @@ -0,0 +1,13 @@ +# Generated by Django 3.2.4 on 2021-08-04 21:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0077_rename_physical_format_edition_physical_format_detail"), + ("bookwyrm", "0079_merge_20210804_1746"), + ] + + operations = [] From ae840002dec008ecaebbf10ed219446d4b8be98d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 27 Aug 2021 12:21:16 -0700 Subject: [PATCH 007/178] Only show update option when there's an active readthrough --- .../templates/snippets/shelve_button/shelve_button_options.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html b/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html index 5cce1477..0bda5860 100644 --- a/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html +++ b/bookwyrm/templates/snippets/shelve_button/shelve_button_options.html @@ -42,7 +42,7 @@ {% endfor %} {% if dropdown %} -{% if readthrough and active_shelf.shelf.identifier != 'read' %} +{% if readthrough and not readthrough.finish_date and active_shelf.shelf.identifier != 'read' %} +
  • + {% url 'settings-ip-blocks' as url %} + {% trans "IP Address Blocklist" %} +
  • {% endif %} {% if perms.bookwyrm.edit_instance_settings %} diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 6acf75cb..af5714d7 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -154,6 +154,16 @@ urlpatterns = [ views.EmailBlocklist.as_view(), name="settings-email-blocks-delete", ), + re_path( + r"^settings/ip-blocklist/?$", + views.IPBlocklist.as_view(), + name="settings-ip-blocks", + ), + re_path( + r"^settings/ip-blocks/(?P\d+)/delete/?$", + views.IPBlocklist.as_view(), + name="settings-ip-blocks-delete", + ), # moderation re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"), re_path( diff --git a/bookwyrm/views/__init__.py b/bookwyrm/views/__init__.py index 95f3fc7f..16ebb2ba 100644 --- a/bookwyrm/views/__init__.py +++ b/bookwyrm/views/__init__.py @@ -5,6 +5,7 @@ from .admin.federation import Federation, FederatedServer from .admin.federation import AddFederatedServer, ImportServerBlocklist from .admin.federation import block_server, unblock_server from .admin.email_blocklist import EmailBlocklist +from .admin.ip_blocklist import IPBlocklist from .admin.invite import ManageInvites, Invite, InviteRequest from .admin.invite import ManageInviteRequests, ignore_invite_request from .admin.reports import ( diff --git a/bookwyrm/views/admin/email_blocklist.py b/bookwyrm/views/admin/email_blocklist.py index 2ed8c152..cd3d83fd 100644 --- a/bookwyrm/views/admin/email_blocklist.py +++ b/bookwyrm/views/admin/email_blocklist.py @@ -1,4 +1,4 @@ -""" moderation via flagged posts and users """ +""" Manage email blocklist""" from django.contrib.auth.decorators import login_required, permission_required from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse @@ -14,7 +14,7 @@ from bookwyrm import forms, models name="dispatch", ) class EmailBlocklist(View): - """Block users by email address""" + """Block registration by email address""" def get(self, request): """view and compose blocks""" diff --git a/bookwyrm/views/admin/ip_blocklist.py b/bookwyrm/views/admin/ip_blocklist.py new file mode 100644 index 00000000..643dae22 --- /dev/null +++ b/bookwyrm/views/admin/ip_blocklist.py @@ -0,0 +1,49 @@ +""" Manage IP blocklist """ +from django.contrib.auth.decorators import login_required, permission_required +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 bookwyrm import forms, models + +# pylint: disable=no-self-use +@method_decorator(login_required, name="dispatch") +@method_decorator( + permission_required("bookwyrm.moderate_user", raise_exception=True), + name="dispatch", +) +class IPBlocklist(View): + """Block registration by ip address""" + + def get(self, request): + """view and compose blocks""" + data = { + "addresses": models.IPBlocklist.objects.all(), + "form": forms.IPBlocklistForm(), + } + return TemplateResponse(request, "settings/ip_blocklist.html", data) + + def post(self, request, block_id=None): + """create a new ip address block""" + if block_id: + return self.delete(request, block_id) + + form = forms.IPBlocklistForm(request.POST) + data = { + "addresses": models.IPBlocklist.objects.all(), + "form": form, + } + if not form.is_valid(): + return TemplateResponse(request, "settings/ip_blocklist.html", data) + form.save() + + data["form"] = forms.IPBlocklistForm() + return TemplateResponse(request, "settings/ip_blocklist.html", data) + + # pylint: disable=unused-argument + def delete(self, request, domain_id): + """remove a domain block""" + domain = get_object_or_404(models.IPBlocklist, id=domain_id) + domain.delete() + return redirect("settings-ip-blocks") From 00892eaaaf2f997132283d62317dd284dcecb51d Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 17 Sep 2021 20:58:09 -0700 Subject: [PATCH 020/178] Temporary fix for pylint --- .github/workflows/pylint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 1b14149f..2a81eaa3 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -24,5 +24,5 @@ jobs: pip install pylint - name: Analysing the code with pylint run: | - pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801 + pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801,C0209 From 377a4e1ef1c5fd0664250d4fde76f132d92b6d69 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 17 Sep 2021 21:39:18 -0700 Subject: [PATCH 021/178] Updating string format syntax part 1 --- bookwyrm/activitypub/base_activity.py | 12 +++---- bookwyrm/activitystreams.py | 4 +-- bookwyrm/emailing.py | 8 ++--- bookwyrm/forms.py | 2 +- bookwyrm/models/user.py | 20 ++++++----- bookwyrm/preview_images.py | 5 +-- bookwyrm/sanitize_html.py | 2 +- bookwyrm/settings.py | 15 ++++---- bookwyrm/signatures.py | 20 +++++------ bookwyrm/suggested_users.py | 4 +-- bookwyrm/urls.py | 52 ++++++++++++++------------- 11 files changed, 73 insertions(+), 71 deletions(-) diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 4f7b55d5..24d383ac 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -101,7 +101,7 @@ class ActivityObject: except KeyError: if field.default == MISSING and field.default_factory == MISSING: raise ActivitySerializerError( - "Missing required field: %s" % field.name + f"Missing required field: {field.name}" ) value = field.default setattr(self, field.name, value) @@ -219,8 +219,8 @@ def set_related_field( model_name, origin_model_name, related_field_name, related_remote_id, data ): """load reverse related fields (editions, attachments) without blocking""" - model = apps.get_model("bookwyrm.%s" % model_name, require_ready=True) - origin_model = apps.get_model("bookwyrm.%s" % origin_model_name, require_ready=True) + model = apps.get_model(f"bookwyrm.{model_name}", require_ready=True) + origin_model = apps.get_model(f"bookwyrm.{origin_model_name}", require_ready=True) with transaction.atomic(): if isinstance(data, str): @@ -234,7 +234,7 @@ def set_related_field( # this must exist because it's the object that triggered this function instance = origin_model.find_existing_by_remote_id(related_remote_id) if not instance: - raise ValueError("Invalid related remote id: %s" % related_remote_id) + raise ValueError(f"Invalid related remote id: {related_remote_id}") # set the origin's remote id on the activity so it will be there when # the model instance is created @@ -265,7 +265,7 @@ def get_model_from_type(activity_type): ] if not model: raise ActivitySerializerError( - 'No model found for activity type "%s"' % activity_type + f'No model found for activity type "{activity_type}"' ) return model[0] @@ -286,7 +286,7 @@ def resolve_remote_id( data = get_data(remote_id) except ConnectorException: raise ActivitySerializerError( - "Could not connect to host for remote_id in: %s" % (remote_id) + f"Could not connect to host for remote_id: {remote_id}" ) # determine the model implicitly, if not provided # or if it's a model with subclasses like Status, check again diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index 10149993..13d56f84 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -16,11 +16,11 @@ class ActivityStream(RedisStore): def stream_id(self, user): """the redis key for this user's instance of this stream""" - return "{}-{}".format(user.id, self.key) + return f"{user.id}-{self.key}" def unread_id(self, user): """the redis key for this user's unread count for this stream""" - return "{}-unread".format(self.stream_id(user)) + return "{}-unread".format(self.stream_id(user)) # pylint: disable=consider-using-f-string def get_rank(self, obj): # pylint: disable=no-self-use """statuses are sorted by date published""" diff --git a/bookwyrm/emailing.py b/bookwyrm/emailing.py index 4f43c69e..10db4f13 100644 --- a/bookwyrm/emailing.py +++ b/bookwyrm/emailing.py @@ -11,7 +11,7 @@ def email_data(): """fields every email needs""" site = models.SiteSettings.objects.get() if site.logo_small: - logo_path = "/images/{}".format(site.logo_small.url) + logo_path = f"/images/{site.logo_small.url}" else: logo_path = "/static/images/logo-small.png" @@ -49,15 +49,15 @@ def password_reset_email(reset_code): def format_email(email_name, data): """render the email templates""" subject = ( - get_template("email/{}/subject.html".format(email_name)).render(data).strip() + get_template(f"email/{email_name}/subject.html").render(data).strip() ) html_content = ( - get_template("email/{}/html_content.html".format(email_name)) + get_template(f"email/{email_name}/html_content.html") .render(data) .strip() ) text_content = ( - get_template("email/{}/text_content.html".format(email_name)) + get_template(f"email/{email_name}/text_content.html") .render(data) .strip() ) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index b53d0b6a..f20cdd00 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -261,7 +261,7 @@ class CreateInviteForm(CustomForm): ), "use_limit": widgets.Select( choices=[ - (i, _("%(count)d uses" % {"count": i})) + (i, _(f"{i} uses")) for i in [1, 5, 10, 25, 50, 100] ] + [(None, _("Unlimited"))] diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index a33daf72..750cfa44 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -152,12 +152,13 @@ class User(OrderedCollectionPageMixin, AbstractUser): @property def following_link(self): """just how to find out the following info""" - return "{:s}/following".format(self.remote_id) + return f"{self.remote_id}/following" @property def alt_text(self): """alt text with username""" - return "avatar for %s" % (self.localname or self.username) + # pylint: disable=consider-using-f-string + return "avatar for {:s}".format(self.localname or self.username) @property def display_name(self): @@ -198,7 +199,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): """an ordered collection of statuses""" if filter_type: filter_class = apps.get_model( - "bookwyrm.%s" % filter_type, require_ready=True + f"bookwyrm.{filter_type}", require_ready=True ) if not issubclass(filter_class, Status): raise TypeError( @@ -223,7 +224,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): def to_following_activity(self, **kwargs): """activitypub following list""" - remote_id = "%s/following" % self.remote_id + remote_id = f"{self.remote_id}/following" return self.to_ordered_collection( self.following.order_by("-updated_date").all(), remote_id=remote_id, @@ -266,7 +267,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): if not self.local and not re.match(regex.FULL_USERNAME, self.username): # generate a username that uses the domain (webfinger format) actor_parts = urlparse(self.remote_id) - self.username = "%s@%s" % (self.username, actor_parts.netloc) + self.username = f"{self.username}@{actor_parts.netloc}" # this user already exists, no need to populate fields if not created: @@ -320,7 +321,8 @@ class User(OrderedCollectionPageMixin, AbstractUser): @property def local_path(self): """this model doesn't inherit bookwyrm model, so here we are""" - return "/user/%s" % (self.localname or self.username) + # pylint: disable=consider-using-f-string + return "/user/{:s}".format(self.localname or self.username) def create_shelves(self): """default shelves for a new user""" @@ -361,7 +363,7 @@ class KeyPair(ActivitypubMixin, BookWyrmModel): def get_remote_id(self): # self.owner is set by the OneToOneField on User - return "%s/#main-key" % self.owner.remote_id + return f"{self.owner.remote_id}/#main-key" def save(self, *args, **kwargs): """create a key pair""" @@ -398,7 +400,7 @@ class AnnualGoal(BookWyrmModel): def get_remote_id(self): """put the year in the path""" - return "{:s}/goal/{:d}".format(self.user.remote_id, self.year) + return f"{self.user.remote_id}/goal/{self.year}" @property def books(self): @@ -454,7 +456,7 @@ def get_or_create_remote_server(domain): pass try: - data = get_data("https://%s/.well-known/nodeinfo" % domain) + data = get_data(f"https://{domain}/.well-known/nodeinfo") try: nodeinfo_url = data.get("links")[0].get("href") except (TypeError, KeyError): diff --git a/bookwyrm/preview_images.py b/bookwyrm/preview_images.py index 900a3e12..c9ef4643 100644 --- a/bookwyrm/preview_images.py +++ b/bookwyrm/preview_images.py @@ -315,7 +315,8 @@ def save_and_cleanup(image, instance=None): """Save and close the file""" if not isinstance(instance, (models.Book, models.User, models.SiteSettings)): return False - file_name = "%s-%s.jpg" % (str(instance.id), str(uuid4())) + uuid = uuid4() + file_name = f"{instance.id}-{uuid}.jpg" image_buffer = BytesIO() try: @@ -412,7 +413,7 @@ def generate_user_preview_image_task(user_id): texts = { "text_one": user.display_name, - "text_three": "@{}@{}".format(user.localname, settings.DOMAIN), + "text_three": "@{user.localname}@{settings.DOMAIN}" } if user.avatar: diff --git a/bookwyrm/sanitize_html.py b/bookwyrm/sanitize_html.py index 0be64c58..8b0e3c4c 100644 --- a/bookwyrm/sanitize_html.py +++ b/bookwyrm/sanitize_html.py @@ -48,7 +48,7 @@ class InputHtmlParser(HTMLParser): # pylint: disable=abstract-method return self.tag_stack = self.tag_stack[:-1] - self.output.append(("tag", "" % tag)) + self.output.append(("tag", f"")) def handle_data(self, data): """extract the answer, if we're in an answer tag""" diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index 452b8d94..521014a1 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -23,7 +23,7 @@ EMAIL_HOST_USER = env("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True) EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False) -DEFAULT_FROM_EMAIL = "admin@{:s}".format(env("DOMAIN")) +DEFAULT_FROM_EMAIL = "admin@{:s}".format(env("DOMAIN")) # pylint: disable=consider-using-f-string # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -177,11 +177,8 @@ USE_L10N = True USE_TZ = True -USER_AGENT = "%s (BookWyrm/%s; +https://%s/)" % ( - requests.utils.default_user_agent(), - VERSION, - DOMAIN, -) +agent = requests.utils.default_user_agent() +USER_AGENT = f"{agent} (BookWyrm/{VERSION}; +https://{DOMAIN}/)" # Imagekit generated thumbnails ENABLE_THUMBNAIL_GENERATION = env.bool("ENABLE_THUMBNAIL_GENERATION", False) @@ -212,11 +209,11 @@ if USE_S3: AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=86400"} # S3 Static settings STATIC_LOCATION = "static" - STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, STATIC_LOCATION) + STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{STATIC_LOCATION}/" STATICFILES_STORAGE = "bookwyrm.storage_backends.StaticStorage" # S3 Media settings MEDIA_LOCATION = "images" - MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, MEDIA_LOCATION) + MEDIA_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIA_LOCATION}/" MEDIA_FULL_URL = MEDIA_URL DEFAULT_FILE_STORAGE = "bookwyrm.storage_backends.ImagesStorage" # I don't know if it's used, but the site crashes without it @@ -226,5 +223,5 @@ else: STATIC_URL = "/static/" STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static")) MEDIA_URL = "/images/" - MEDIA_FULL_URL = "%s://%s%s" % (PROTOCOL, DOMAIN, MEDIA_URL) + MEDIA_FULL_URL = f"{PROTOCOL}://{DOMAIN}{MEDIA_URL}" MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images")) diff --git a/bookwyrm/signatures.py b/bookwyrm/signatures.py index c8c90028..61cafe71 100644 --- a/bookwyrm/signatures.py +++ b/bookwyrm/signatures.py @@ -26,21 +26,21 @@ def make_signature(sender, destination, date, digest): """uses a private key to sign an outgoing message""" inbox_parts = urlparse(destination) signature_headers = [ - "(request-target): post %s" % inbox_parts.path, - "host: %s" % inbox_parts.netloc, - "date: %s" % date, - "digest: %s" % digest, + f"(request-target): post {inbox_parts.path}", + f"host: {inbox_parts.netloc}", + f"date: {date}", + f"digest: {digest}", ] message_to_sign = "\n".join(signature_headers) signer = pkcs1_15.new(RSA.import_key(sender.key_pair.private_key)) signed_message = signer.sign(SHA256.new(message_to_sign.encode("utf8"))) signature = { - "keyId": "%s#main-key" % sender.remote_id, + "keyId": f"{sender.remote_id}#main-key", "algorithm": "rsa-sha256", "headers": "(request-target) host date digest", "signature": b64encode(signed_message).decode("utf8"), } - return ",".join('%s="%s"' % (k, v) for (k, v) in signature.items()) + return ",".join(f'{k}="{v}"' for (k, v) in signature.items()) def make_digest(data): @@ -58,7 +58,7 @@ def verify_digest(request): elif algorithm == "SHA-512": hash_function = hashlib.sha512 else: - raise ValueError("Unsupported hash function: {}".format(algorithm)) + raise ValueError(f"Unsupported hash function: {algorithm}") expected = hash_function(request.body).digest() if b64decode(digest) != expected: @@ -95,18 +95,18 @@ class Signature: def verify(self, public_key, request): """verify rsa signature""" if http_date_age(request.headers["date"]) > MAX_SIGNATURE_AGE: - raise ValueError("Request too old: %s" % (request.headers["date"],)) + raise ValueError(f"Request too old: {request.headers['date']}") public_key = RSA.import_key(public_key) comparison_string = [] for signed_header_name in self.headers.split(" "): if signed_header_name == "(request-target)": - comparison_string.append("(request-target): post %s" % request.path) + comparison_string.append(f"(request-target): post {request.path}") else: if signed_header_name == "digest": verify_digest(request) comparison_string.append( - "%s: %s" % (signed_header_name, request.headers[signed_header_name]) + f"{signed_header_name}: {request.headers[signed_header_name]}" ) comparison_string = "\n".join(comparison_string) diff --git a/bookwyrm/suggested_users.py b/bookwyrm/suggested_users.py index 88343061..e8f23632 100644 --- a/bookwyrm/suggested_users.py +++ b/bookwyrm/suggested_users.py @@ -24,8 +24,8 @@ class SuggestedUsers(RedisStore): def store_id(self, user): # pylint: disable=no-self-use """the key used to store this user's recs""" if isinstance(user, int): - return "{:d}-suggestions".format(user) - return "{:d}-suggestions".format(user.id) + return f"{user}-suggestions" + return f"{user.id}-suggestions" def get_counts_from_rank(self, rank): # pylint: disable=no-self-use """calculate mutuals count and shared books count from rank""" diff --git a/bookwyrm/urls.py b/bookwyrm/urls.py index 6acf75cb..6ff0c183 100644 --- a/bookwyrm/urls.py +++ b/bookwyrm/urls.py @@ -7,8 +7,8 @@ from django.views.generic.base import TemplateView from bookwyrm import settings, views from bookwyrm.utils import regex -USER_PATH = r"^user/(?P%s)" % regex.USERNAME -LOCAL_USER_PATH = r"^user/(?P%s)" % regex.LOCALNAME +USER_PATH = rf"^user/(?P{regex.USERNAME})" +LOCAL_USER_PATH = rf"^user/(?P{regex.LOCALNAME})" status_types = [ "status", @@ -19,7 +19,9 @@ status_types = [ "boost", "generatednote", ] -STATUS_PATH = r"%s/(%s)/(?P\d+)" % (USER_PATH, "|".join(status_types)) + +status_types_string = "|".join(status_types) +STATUS_PATH = rf"{USER_PATH}/({status_types_string})/(?P\d+)" BOOK_PATH = r"^book/(?P\d+)" @@ -33,8 +35,8 @@ urlpatterns = [ ), # federation endpoints re_path(r"^inbox/?$", views.Inbox.as_view()), - re_path(r"%s/inbox/?$" % LOCAL_USER_PATH, views.Inbox.as_view()), - re_path(r"%s/outbox/?$" % LOCAL_USER_PATH, views.Outbox.as_view()), + re_path(rf"{LOCAL_USER_PATH}/inbox/?$", views.Inbox.as_view()), + re_path(rf"{LOCAL_USER_PATH}/outbox/?$", views.Outbox.as_view()), re_path(r"^\.well-known/webfinger/?$", views.webfinger), re_path(r"^\.well-known/nodeinfo/?$", views.nodeinfo_pointer), re_path(r"^\.well-known/host-meta/?$", views.host_meta), @@ -210,12 +212,12 @@ urlpatterns = [ name="get-started-users", ), # feeds - re_path(r"^(?P{:s})/?$".format(STREAMS), views.Feed.as_view()), + re_path(rf"^(?P{STREAMS})/?$", views.Feed.as_view()), re_path( r"^direct-messages/?$", views.DirectMessage.as_view(), name="direct-messages" ), re_path( - r"^direct-messages/(?P%s)?$" % regex.USERNAME, + rf"^direct-messages/(?P{regex.USERNAME})?$", views.DirectMessage.as_view(), name="direct-messages-user", ), @@ -225,22 +227,22 @@ urlpatterns = [ re_path(r"^import/?$", views.Import.as_view(), name="import"), re_path(r"^import/(\d+)/?$", views.ImportStatus.as_view(), name="import-status"), # users - re_path(r"%s\.json$" % USER_PATH, views.User.as_view()), - re_path(r"%s/?$" % USER_PATH, views.User.as_view(), name="user-feed"), - re_path(r"%s/rss" % USER_PATH, views.rss_feed.RssFeed(), name="user-rss"), + re_path(rf"{USER_PATH}\.json$", views.User.as_view()), + re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"), + re_path(rf"{USER_PATH}/rss/?$", views.rss_feed.RssFeed(), name="user-rss"), re_path( - r"%s/followers(.json)?/?$" % USER_PATH, + rf"{USER_PATH}/followers(.json)?/?$", views.Followers.as_view(), name="user-followers", ), re_path( - r"%s/following(.json)?/?$" % USER_PATH, + rf"{USER_PATH}/following(.json)?/?$", views.Following.as_view(), name="user-following", ), re_path(r"^hide-suggestions/?$", views.hide_suggestions, name="hide-suggestions"), # lists - re_path(r"%s/lists/?$" % USER_PATH, views.UserLists.as_view(), name="user-lists"), + re_path(rf"{USER_PATH}/lists/?$", views.UserLists.as_view(), name="user-lists"), re_path(r"^list/?$", views.Lists.as_view(), name="lists"), re_path(r"^list/saved/?$", views.SavedLists.as_view(), name="saved-lists"), re_path(r"^list/(?P\d+)(.json)?/?$", views.List.as_view(), name="list"), @@ -262,14 +264,14 @@ urlpatterns = [ re_path(r"^save-list/(?P\d+)/?$", views.save_list, name="list-save"), re_path(r"^unsave-list/(?P\d+)/?$", views.unsave_list, name="list-unsave"), # User books - re_path(r"%s/books/?$" % USER_PATH, views.Shelf.as_view(), name="user-shelves"), + re_path(rf"{USER_PATH}/books/?$", views.Shelf.as_view(), name="user-shelves"), re_path( - r"^%s/(helf|books)/(?P[\w-]+)(.json)?/?$" % USER_PATH, + rf"^{USER_PATH}/(helf|books)/(?P[\w-]+)(.json)?/?$", views.Shelf.as_view(), name="shelf", ), re_path( - r"^%s/(books|shelf)/(?P[\w-]+)(.json)?/?$" % LOCAL_USER_PATH, + rf"^{LOCAL_USER_PATH}/(books|shelf)/(?P[\w-]+)(.json)?/?$", views.Shelf.as_view(), name="shelf", ), @@ -279,7 +281,7 @@ urlpatterns = [ re_path(r"^unshelve/?$", views.unshelve), # goals re_path( - r"%s/goal/(?P\d{4})/?$" % USER_PATH, + rf"{USER_PATH}/goal/(?P\d{4})/?$", views.Goal.as_view(), name="user-goal", ), @@ -296,10 +298,10 @@ urlpatterns = [ re_path(r"^block/(?P\d+)/?$", views.Block.as_view()), re_path(r"^unblock/(?P\d+)/?$", views.unblock), # statuses - re_path(r"%s(.json)?/?$" % STATUS_PATH, views.Status.as_view(), name="status"), - re_path(r"%s/activity/?$" % STATUS_PATH, views.Status.as_view(), name="status"), + re_path(rf"{STATUS_PATH}(.json)?/?$", views.Status.as_view(), name="status"), + re_path(rf"{STATUS_PATH}/activity/?$", views.Status.as_view(), name="status"), re_path( - r"%s/replies(.json)?/?$" % STATUS_PATH, views.Replies.as_view(), name="replies" + rf"{STATUS_PATH}/replies(.json)?/?$", views.Replies.as_view(), name="replies" ), re_path( r"^post/?$", @@ -329,17 +331,17 @@ urlpatterns = [ re_path(r"^boost/(?P\d+)/?$", views.Boost.as_view()), re_path(r"^unboost/(?P\d+)/?$", views.Unboost.as_view()), # books - re_path(r"%s(.json)?/?$" % BOOK_PATH, views.Book.as_view(), name="book"), + re_path(rf"{BOOK_PATH}(.json)?/?$", views.Book.as_view(), name="book"), re_path( - r"%s/(?Preview|comment|quote)/?$" % BOOK_PATH, + rf"{BOOK_PATH}/(?Preview|comment|quote)/?$", views.Book.as_view(), name="book-user-statuses", ), - re_path(r"%s/edit/?$" % BOOK_PATH, views.EditBook.as_view(), name="edit-book"), - re_path(r"%s/confirm/?$" % BOOK_PATH, views.ConfirmEditBook.as_view()), + re_path(rf"{BOOK_PATH}/edit/?$", views.EditBook.as_view(), name="edit-book"), + re_path(rf"{BOOK_PATH}/confirm/?$", views.ConfirmEditBook.as_view()), re_path(r"^create-book/?$", views.EditBook.as_view(), name="create-book"), re_path(r"^create-book/confirm?$", views.ConfirmEditBook.as_view()), - re_path(r"%s/editions(.json)?/?$" % BOOK_PATH, views.Editions.as_view()), + re_path(rf"{BOOK_PATH}/editions(.json)?/?$", views.Editions.as_view()), re_path( r"^upload-cover/(?P\d+)/?$", views.upload_cover, name="upload-cover" ), From acfb1bb3764c7a41c699aebc13da9c61418e239e Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 11:32:00 -0700 Subject: [PATCH 022/178] Updating string format synatx part 2 --- .github/workflows/pylint.yml | 2 +- bookwyrm/connectors/abstract_connector.py | 9 +++++---- bookwyrm/connectors/connector_manager.py | 12 ++++++------ bookwyrm/connectors/inventaire.py | 22 +++++++-------------- bookwyrm/connectors/openlibrary.py | 14 ++++++------- bookwyrm/connectors/self_connector.py | 2 +- bookwyrm/importers/importer.py | 1 + bookwyrm/models/activitypub_mixin.py | 24 +++++++++++------------ bookwyrm/models/author.py | 2 +- bookwyrm/models/base_model.py | 8 ++++---- bookwyrm/models/book.py | 9 +++++---- bookwyrm/models/connector.py | 5 +---- bookwyrm/models/fields.py | 6 +++--- bookwyrm/models/import_job.py | 2 ++ bookwyrm/models/list.py | 2 +- bookwyrm/models/relationship.py | 5 +++-- bookwyrm/models/shelf.py | 4 ++-- bookwyrm/models/site.py | 4 ++-- bookwyrm/models/status.py | 19 +++++------------- bookwyrm/templatetags/utilities.py | 6 +++--- bookwyrm/utils/regex.py | 6 +++--- bookwyrm/views/author.py | 2 +- bookwyrm/views/edit_user.py | 2 +- bookwyrm/views/editions.py | 2 +- bookwyrm/views/feed.py | 2 +- bookwyrm/views/follow.py | 2 +- bookwyrm/views/helpers.py | 2 +- bookwyrm/views/import_data.py | 4 ++-- bookwyrm/views/inbox.py | 2 +- bookwyrm/views/list.py | 4 ++-- bookwyrm/views/login.py | 2 +- bookwyrm/views/password.py | 2 +- bookwyrm/views/register.py | 2 +- bookwyrm/views/search.py | 2 +- bookwyrm/views/status.py | 8 ++++---- bookwyrm/views/wellknown.py | 4 ++-- 36 files changed, 96 insertions(+), 110 deletions(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 2a81eaa3..1b14149f 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -24,5 +24,5 @@ jobs: pip install pylint - name: Analysing the code with pylint run: | - pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801,C0209 + pylint bookwyrm/ --ignore=migrations,tests --disable=E1101,E1135,E1136,R0903,R0901,R0902,W0707,W0511,W0406,R0401,R0801 diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index ffacffdf..902ccd6c 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -43,7 +43,7 @@ class AbstractMinimalConnector(ABC): params["min_confidence"] = min_confidence data = self.get_search_data( - "%s%s" % (self.search_url, query), + f"{self.search_url}{query}", params=params, timeout=timeout, ) @@ -57,7 +57,7 @@ class AbstractMinimalConnector(ABC): """isbn search""" params = {} data = self.get_search_data( - "%s%s" % (self.isbn_search_url, query), + f"{self.isbn_search_url}{query}", params=params, ) results = [] @@ -131,7 +131,7 @@ class AbstractConnector(AbstractMinimalConnector): work_data = data if not work_data or not edition_data: - raise ConnectorException("Unable to load book data: %s" % remote_id) + raise ConnectorException(f"Unable to load book data: {remote_id}") with transaction.atomic(): # create activitypub object @@ -223,7 +223,7 @@ def get_data(url, params=None, timeout=10): # check if the url is blocked if models.FederatedServer.is_blocked(url): raise ConnectorException( - "Attempting to load data from blocked url: {:s}".format(url) + f"Attempting to load data from blocked url: {url}" ) try: @@ -283,6 +283,7 @@ class SearchResult: confidence: int = 1 def __repr__(self): + # pylint: disable=consider-using-f-string return "".format( self.key, self.title, self.author ) diff --git a/bookwyrm/connectors/connector_manager.py b/bookwyrm/connectors/connector_manager.py index 1d9588d6..b676e9aa 100644 --- a/bookwyrm/connectors/connector_manager.py +++ b/bookwyrm/connectors/connector_manager.py @@ -109,10 +109,10 @@ def get_or_create_connector(remote_id): connector_info = models.Connector.objects.create( identifier=identifier, connector_file="bookwyrm_connector", - base_url="https://%s" % identifier, - books_url="https://%s/book" % identifier, - covers_url="https://%s/images/covers" % identifier, - search_url="https://%s/search?q=" % identifier, + base_url=f"https://{identifier}", + books_url=f"https://{identifier}/book", + covers_url=f"https://{identifier}/images/covers", + search_url=f"https://{identifier}/search?q=", priority=2, ) @@ -131,7 +131,7 @@ def load_more_data(connector_id, book_id): def load_connector(connector_info): """instantiate the connector class""" connector = importlib.import_module( - "bookwyrm.connectors.%s" % connector_info.connector_file + f"bookwyrm.connectors.{connector_info.connector_file}" ) return connector.Connector(connector_info.identifier) @@ -141,4 +141,4 @@ def load_connector(connector_info): def create_connector(sender, instance, created, *args, **kwargs): """create a connector to an external bookwyrm server""" if instance.application_type == "bookwyrm": - get_or_create_connector("https://{:s}".format(instance.server_name)) + get_or_create_connector(f"https://{instance.server_name}") diff --git a/bookwyrm/connectors/inventaire.py b/bookwyrm/connectors/inventaire.py index d2a7b9fa..b23a7d52 100644 --- a/bookwyrm/connectors/inventaire.py +++ b/bookwyrm/connectors/inventaire.py @@ -59,7 +59,7 @@ class Connector(AbstractConnector): def get_remote_id(self, value): """convert an id/uri into a url""" - return "{:s}?action=by-uris&uris={:s}".format(self.books_url, value) + return f"{self.books_url}?action=by-uris&uris={value}" def get_book_data(self, remote_id): data = get_data(remote_id) @@ -88,7 +88,7 @@ class Connector(AbstractConnector): def format_search_result(self, search_result): images = search_result.get("image") cover = ( - "{:s}/img/entities/{:s}".format(self.covers_url, images[0]) + f"{self.covers_url}/img/entities/{images[0]}" if images else None ) @@ -99,9 +99,7 @@ class Connector(AbstractConnector): title=search_result.get("label"), key=self.get_remote_id(search_result.get("uri")), author=search_result.get("description"), - view_link="{:s}/entity/{:s}".format( - self.base_url, search_result.get("uri") - ), + view_link=f"{self.base_url}/entity/{search_result.get('uri')}", cover=cover, confidence=confidence, connector=self, @@ -123,9 +121,7 @@ class Connector(AbstractConnector): title=title[0], key=self.get_remote_id(search_result.get("uri")), author=search_result.get("description"), - view_link="{:s}/entity/{:s}".format( - self.base_url, search_result.get("uri") - ), + view_link=f"{self.base_url}/entity/{search_result.get('uri')}", cover=self.get_cover_url(search_result.get("image")), connector=self, ) @@ -136,9 +132,7 @@ class Connector(AbstractConnector): def load_edition_data(self, work_uri): """get a list of editions for a work""" url = ( - "{:s}?action=reverse-claims&property=wdt:P629&value={:s}&sort=true".format( - self.books_url, work_uri - ) + f"{self.books_url}?action=reverse-claims&property=wdt:P629&value={work_uri}&sort=true" ) return get_data(url) @@ -195,7 +189,7 @@ class Connector(AbstractConnector): # cover may or may not be an absolute url already if re.match(r"^http", cover_id): return cover_id - return "%s%s" % (self.covers_url, cover_id) + return f"{self.covers_url}{cover_id}" def resolve_keys(self, keys): """cool, it's "wd:Q3156592" now what the heck does that mean""" @@ -213,9 +207,7 @@ class Connector(AbstractConnector): link = links.get("enwiki") if not link: return "" - url = "{:s}/api/data?action=wp-extract&lang=en&title={:s}".format( - self.base_url, link - ) + url = f"{self.base_url}/api/data?action=wp-extract&lang=en&title={link}" try: data = get_data(url) except ConnectorException: diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index e58749c1..fca5d0f7 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -71,7 +71,7 @@ class Connector(AbstractConnector): key = data["key"] except KeyError: raise ConnectorException("Invalid book data") - return "%s%s" % (self.books_url, key) + return f"{self.books_url}{key}" def is_work_data(self, data): return bool(re.match(r"^[\/\w]+OL\d+W$", data["key"])) @@ -81,7 +81,7 @@ class Connector(AbstractConnector): key = data["key"] except KeyError: raise ConnectorException("Invalid book data") - url = "%s%s/editions" % (self.books_url, key) + url = f"{self.books_url}{key}/editions" data = self.get_book_data(url) edition = pick_default_edition(data["entries"]) if not edition: @@ -93,7 +93,7 @@ class Connector(AbstractConnector): key = data["works"][0]["key"] except (IndexError, KeyError): raise ConnectorException("No work found for edition") - url = "%s%s" % (self.books_url, key) + url = f"{self.books_url}{key}" return self.get_book_data(url) def get_authors_from_data(self, data): @@ -102,7 +102,7 @@ class Connector(AbstractConnector): author_blob = author_blob.get("author", author_blob) # this id is "/authors/OL1234567A" author_id = author_blob["key"] - url = "%s%s" % (self.base_url, author_id) + url = f"{self.base_url}{author_id}" author = self.get_or_create_author(url) if not author: continue @@ -113,8 +113,8 @@ class Connector(AbstractConnector): if not cover_blob: return None cover_id = cover_blob[0] - image_name = "%s-%s.jpg" % (cover_id, size) - return "%s/b/id/%s" % (self.covers_url, image_name) + image_name = f"{cover_id}-{size}.jpg" + return f"{self.covers_url}/b/id/{image_name}" def parse_search_data(self, data): return data.get("docs") @@ -152,7 +152,7 @@ class Connector(AbstractConnector): def load_edition_data(self, olkey): """query openlibrary for editions of a work""" - url = "%s/works/%s/editions" % (self.books_url, olkey) + url = f"{self.books_url}/works/{olkey}/editions" return self.get_book_data(url) def expand_book_data(self, book): diff --git a/bookwyrm/connectors/self_connector.py b/bookwyrm/connectors/self_connector.py index 8d5a7614..cdb586cb 100644 --- a/bookwyrm/connectors/self_connector.py +++ b/bookwyrm/connectors/self_connector.py @@ -71,7 +71,7 @@ class Connector(AbstractConnector): def format_search_result(self, search_result): cover = None if search_result.cover: - cover = "%s%s" % (self.covers_url, search_result.cover) + cover = f"{self.covers_url}{search_result.cover}" return SearchResult( title=search_result.title, diff --git a/bookwyrm/importers/importer.py b/bookwyrm/importers/importer.py index caa137ec..a10b4060 100644 --- a/bookwyrm/importers/importer.py +++ b/bookwyrm/importers/importer.py @@ -127,6 +127,7 @@ def handle_imported_book(source, user, item, include_reviews, privacy): # but "now" is a bad guess published_date_guess = item.date_read or item.date_added if item.review: + # pylint: disable=consider-using-f-string review_title = ( "Review of {!r} on {!r}".format( item.book.title, diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py index ed51158b..3a88c524 100644 --- a/bookwyrm/models/activitypub_mixin.py +++ b/bookwyrm/models/activitypub_mixin.py @@ -266,7 +266,7 @@ class ObjectMixin(ActivitypubMixin): signed_message = signer.sign(SHA256.new(content.encode("utf8"))) signature = activitypub.Signature( - creator="%s#main-key" % user.remote_id, + creator=f"{user.remote_id}#main-key", created=activity_object.published, signatureValue=b64encode(signed_message).decode("utf8"), ) @@ -285,16 +285,16 @@ class ObjectMixin(ActivitypubMixin): return activitypub.Delete( id=self.remote_id + "/activity", actor=user.remote_id, - to=["%s/followers" % user.remote_id], + to=[f"{user.remote_id}/followers"], cc=["https://www.w3.org/ns/activitystreams#Public"], object=self, ).serialize() def to_update_activity(self, user): """wrapper for Updates to an activity""" - activity_id = "%s#update/%s" % (self.remote_id, uuid4()) + uuid = uuid4() return activitypub.Update( - id=activity_id, + id=f"{self.remote_id}#update/{uuid}", actor=user.remote_id, to=["https://www.w3.org/ns/activitystreams#Public"], object=self, @@ -337,8 +337,8 @@ class OrderedCollectionPageMixin(ObjectMixin): paginated = Paginator(queryset, PAGE_LENGTH) # add computed fields specific to orderd collections activity["totalItems"] = paginated.count - activity["first"] = "%s?page=1" % remote_id - activity["last"] = "%s?page=%d" % (remote_id, paginated.num_pages) + activity["first"] = f"{remote_id}?page=1" + activity["last"] = f"{remote_id}?page={paginated.num_pages}" return serializer(**activity) @@ -420,7 +420,7 @@ class CollectionItemMixin(ActivitypubMixin): """AP for shelving a book""" collection_field = getattr(self, self.collection_field) return activitypub.Add( - id="{:s}#add".format(collection_field.remote_id), + id=f"{collection_field.remote_id}#add", actor=user.remote_id, object=self.to_activity_dataclass(), target=collection_field.remote_id, @@ -430,7 +430,7 @@ class CollectionItemMixin(ActivitypubMixin): """AP for un-shelving a book""" collection_field = getattr(self, self.collection_field) return activitypub.Remove( - id="{:s}#remove".format(collection_field.remote_id), + id=f"{collection_field.remote_id}#remove", actor=user.remote_id, object=self.to_activity_dataclass(), target=collection_field.remote_id, @@ -458,7 +458,7 @@ class ActivityMixin(ActivitypubMixin): """undo an action""" user = self.user if hasattr(self, "user") else self.user_subject return activitypub.Undo( - id="%s#undo" % self.remote_id, + id=f"{self.remote_id}#undo", actor=user.remote_id, object=self, ).serialize() @@ -555,11 +555,11 @@ def to_ordered_collection_page( prev_page = next_page = None if activity_page.has_next(): - next_page = "%s?page=%d" % (remote_id, activity_page.next_page_number()) + next_page = f"{remote_id}?page={activity_page.next_page_number()}" if activity_page.has_previous(): - prev_page = "%s?page=%d" % (remote_id, activity_page.previous_page_number()) + prev_page = f"{remote_id}?page=%d{activity_page.previous_page_number()}" return activitypub.OrderedCollectionPage( - id="%s?page=%s" % (remote_id, page), + id=f"{remote_id}?page={page}", partOf=remote_id, orderedItems=items, next=next_page, diff --git a/bookwyrm/models/author.py b/bookwyrm/models/author.py index 6da80b17..53cf94ff 100644 --- a/bookwyrm/models/author.py +++ b/bookwyrm/models/author.py @@ -35,7 +35,7 @@ class Author(BookDataModel): def get_remote_id(self): """editions and works both use "book" instead of model_name""" - return "https://%s/author/%s" % (DOMAIN, self.id) + return f"https://{DOMAIN}/author/{self.id}" activity_serializer = activitypub.Author diff --git a/bookwyrm/models/base_model.py b/bookwyrm/models/base_model.py index 0ac2061a..aa174a14 100644 --- a/bookwyrm/models/base_model.py +++ b/bookwyrm/models/base_model.py @@ -32,11 +32,11 @@ class BookWyrmModel(models.Model): def get_remote_id(self): """generate a url that resolves to the local object""" - base_path = "https://%s" % DOMAIN + base_path = f"https://{DOMAIN}" if hasattr(self, "user"): - base_path = "%s%s" % (base_path, self.user.local_path) + base_path = f"{base_path}{self.user.local_path}" model_name = type(self).__name__.lower() - return "%s/%s/%d" % (base_path, model_name, self.id) + return f"{base_path}/{model_name}/{self.id}" class Meta: """this is just here to provide default fields for other models""" @@ -46,7 +46,7 @@ class BookWyrmModel(models.Model): @property def local_path(self): """how to link to this object in the local app""" - return self.get_remote_id().replace("https://%s" % DOMAIN, "") + return self.get_remote_id().replace(f"https://{DOMAIN}", "") def visible_to_user(self, viewer): """is a user authorized to view an object?""" diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index ec1c14ec..ac7c42f6 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -164,9 +164,9 @@ class Book(BookDataModel): @property def alt_text(self): """image alt test""" - text = "%s" % self.title + text = self.title if self.edition_info: - text += " (%s)" % self.edition_info + text += f" ({self.edition_info})" return text def save(self, *args, **kwargs): @@ -177,9 +177,10 @@ class Book(BookDataModel): def get_remote_id(self): """editions and works both use "book" instead of model_name""" - return "https://%s/book/%d" % (DOMAIN, self.id) + return f"https://{DOMAIN}/book/{self.id}" def __repr__(self): + # pylint: disable=consider-using-f-string return "<{} key={!r} title={!r}>".format( self.__class__, self.openlibrary_key, @@ -216,7 +217,7 @@ class Work(OrderedCollectionPageMixin, Book): """an ordered collection of editions""" return self.to_ordered_collection( self.editions.order_by("-edition_rank").all(), - remote_id="%s/editions" % self.remote_id, + remote_id=f"{self.remote_id}/editions", **kwargs, ) diff --git a/bookwyrm/models/connector.py b/bookwyrm/models/connector.py index 17ba3148..9d2c6aeb 100644 --- a/bookwyrm/models/connector.py +++ b/bookwyrm/models/connector.py @@ -29,7 +29,4 @@ class Connector(BookWyrmModel): isbn_search_url = models.CharField(max_length=255, null=True, blank=True) def __str__(self): - return "{} ({})".format( - self.identifier, - self.id, - ) + return f"{self.identifier} ({self.id})" diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index cc5a7bb5..8555e06f 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -308,7 +308,7 @@ class ManyToManyField(ActivitypubFieldMixin, models.ManyToManyField): def field_to_activity(self, value): if self.link_only: - return "%s/%s" % (value.instance.remote_id, self.name) + return f"{value.instance.remote_id}/{self.name}" return [i.remote_id for i in value.all()] def field_from_activity(self, value): @@ -388,7 +388,7 @@ def image_serializer(value, alt): else: return None if not url[:4] == "http": - url = "https://{:s}{:s}".format(DOMAIN, url) + url = f"https://{DOMAIN}{url}" return activitypub.Document(url=url, name=alt) @@ -448,7 +448,7 @@ class ImageField(ActivitypubFieldMixin, models.ImageField): image_content = ContentFile(response.content) extension = imghdr.what(None, image_content.read()) or "" - image_name = "{:s}.{:s}".format(str(uuid4()), extension) + image_name = f"{uuid4()}.{extension}" return [image_name, image_content] def formfield(self, **kwargs): diff --git a/bookwyrm/models/import_job.py b/bookwyrm/models/import_job.py index 7d8ce9e3..4f495f14 100644 --- a/bookwyrm/models/import_job.py +++ b/bookwyrm/models/import_job.py @@ -198,7 +198,9 @@ class ImportItem(models.Model): return [] def __repr__(self): + # pylint: disable=consider-using-f-string return "<{!r}Item {!r}>".format(self.data["import_source"], self.data["Title"]) def __str__(self): + # pylint: disable=consider-using-f-string return "{} by {}".format(self.data["Title"], self.data["Author"]) diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py index bbad5ba9..49fb5375 100644 --- a/bookwyrm/models/list.py +++ b/bookwyrm/models/list.py @@ -42,7 +42,7 @@ class List(OrderedCollectionMixin, BookWyrmModel): def get_remote_id(self): """don't want the user to be in there in this case""" - return "https://%s/list/%d" % (DOMAIN, self.id) + return f"https://{DOMAIN}/list/{self.id}" @property def collection_queryset(self): diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py index edb89d13..fc7a9df8 100644 --- a/bookwyrm/models/relationship.py +++ b/bookwyrm/models/relationship.py @@ -53,7 +53,7 @@ class UserRelationship(BookWyrmModel): def get_remote_id(self): """use shelf identifier in remote_id""" base_path = self.user_subject.remote_id - return "%s#follows/%d" % (base_path, self.id) + return f"{base_path}#follows/{self.id}" class UserFollows(ActivityMixin, UserRelationship): @@ -144,7 +144,8 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship): """get id for sending an accept or reject of a local user""" base_path = self.user_object.remote_id - return "%s#%s/%d" % (base_path, status, self.id or 0) + status_id = self.id or 0 + return f"{base_path}#{status}/{status_id}" def accept(self, broadcast_only=False): """turn this request into the real deal""" diff --git a/bookwyrm/models/shelf.py b/bookwyrm/models/shelf.py index c4e907d2..6f134402 100644 --- a/bookwyrm/models/shelf.py +++ b/bookwyrm/models/shelf.py @@ -44,7 +44,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel): def get_identifier(self): """custom-shelf-123 for the url""" slug = re.sub(r"[^\w]", "", self.name).lower() - return "{:s}-{:d}".format(slug, self.id) + return f"{slug}-{self.id}" @property def collection_queryset(self): @@ -55,7 +55,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel): """shelf identifier instead of id""" base_path = self.user.remote_id identifier = self.identifier or self.get_identifier() - return "%s/books/%s" % (base_path, identifier) + return f"{base_path}/books/{identifier}" class Meta: """user/shelf unqiueness""" diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index ee69a507..03d6f5fe 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -81,7 +81,7 @@ class SiteInvite(models.Model): @property def link(self): """formats the invite link""" - return "https://{}/invite/{}".format(DOMAIN, self.code) + return f"https://{DOMAIN}/invite/{self.code}" class InviteRequest(BookWyrmModel): @@ -121,7 +121,7 @@ class PasswordReset(models.Model): @property def link(self): """formats the invite link""" - return "https://{}/password-reset/{}".format(DOMAIN, self.code) + return "https://{DOMAIN}/password-reset/{self.code}" # pylint: disable=unused-argument diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index d0f094d2..0b6e49e2 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -179,7 +179,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): """helper function for loading AP serialized replies to a status""" return self.to_ordered_collection( self.replies(self), - remote_id="%s/replies" % self.remote_id, + remote_id=f"{self.remote_id}/replies", collection_only=True, **kwargs ).serialize() @@ -226,10 +226,10 @@ class GeneratedNote(Status): """indicate the book in question for mastodon (or w/e) users""" message = self.content books = ", ".join( - '"%s"' % (book.remote_id, book.title) + f'"{book.title}"' for book in self.mention_books.all() ) - return "%s %s %s" % (self.user.display_name, message, books) + return f"{self.user.display_name} {message} {books}" activity_serializer = activitypub.GeneratedNote pure_type = "Note" @@ -277,11 +277,7 @@ class Comment(BookStatus): @property def pure_content(self): """indicate the book in question for mastodon (or w/e) users""" - return '%s

    (comment on "%s")

    ' % ( - self.content, - self.book.remote_id, - self.book.title, - ) + return f'{self.content}

    (comment on "{self.book.title}")

    ' activity_serializer = activitypub.Comment @@ -306,12 +302,7 @@ class Quotation(BookStatus): """indicate the book in question for mastodon (or w/e) users""" quote = re.sub(r"^

    ", '

    "', self.quote) quote = re.sub(r"

    $", '"

    ', quote) - return '%s

    -- "%s"

    %s' % ( - quote, - self.book.remote_id, - self.book.title, - self.content, - ) + return f'{quote}

    -- "{self.book.title}"

    {self.content}' activity_serializer = activitypub.Quotation diff --git a/bookwyrm/templatetags/utilities.py b/bookwyrm/templatetags/utilities.py index fe83278a..76b4cd3f 100644 --- a/bookwyrm/templatetags/utilities.py +++ b/bookwyrm/templatetags/utilities.py @@ -12,7 +12,7 @@ register = template.Library() @register.filter(name="uuid") def get_uuid(identifier): """for avoiding clashing ids when there are many forms""" - return "%s%s" % (identifier, uuid4()) + return f"{identifier}{uuid4()}" @register.filter(name="username") @@ -50,7 +50,7 @@ def truncatepath(value, arg): length = int(arg) except ValueError: # invalid literal for int() return path_list[-1] # Fail silently. - return "%s/…%s" % (path_list[0], path_list[-1][-length:]) + return f"{path_list[0]}/…{path_list[-1][-length:]}" @register.simple_tag(takes_context=False) @@ -60,7 +60,7 @@ def get_book_cover_thumbnail(book, size="medium", ext="jpg"): if size == "": size = "medium" try: - cover_thumbnail = getattr(book, "cover_bw_book_%s_%s" % (size, ext)) + cover_thumbnail = getattr(book, f"cover_bw_book_{size}_{ext}") return cover_thumbnail.url except OSError: return static("images/no_cover.jpg") diff --git a/bookwyrm/utils/regex.py b/bookwyrm/utils/regex.py index 3ac5a0ff..f0c44245 100644 --- a/bookwyrm/utils/regex.py +++ b/bookwyrm/utils/regex.py @@ -3,8 +3,8 @@ DOMAIN = r"[\w_\-\.]+\.[a-z]{2,}" LOCALNAME = r"@?[a-zA-Z_\-\.0-9]+" STRICT_LOCALNAME = r"@[a-zA-Z_\-\.0-9]+" -USERNAME = r"%s(@%s)?" % (LOCALNAME, DOMAIN) -STRICT_USERNAME = r"\B%s(@%s)?\b" % (STRICT_LOCALNAME, DOMAIN) -FULL_USERNAME = r"%s@%s\b" % (LOCALNAME, DOMAIN) +USERNAME = rf"{LOCALNAME}(@{DOMAIN})?" +STRICT_USERNAME = rf"\B{STRICT_LOCALNAME}(@{DOMAIN})?\b" +FULL_USERNAME = rf"{LOCALNAME}@{DOMAIN}\b" # should match (BookWyrm/1.0.0; or (BookWyrm/99.1.2; BOOKWYRM_USER_AGENT = r"\(BookWyrm/[0-9]+\.[0-9]+\.[0-9]+;" diff --git a/bookwyrm/views/author.py b/bookwyrm/views/author.py index e51dc7d8..e1e9247d 100644 --- a/bookwyrm/views/author.py +++ b/bookwyrm/views/author.py @@ -55,4 +55,4 @@ class EditAuthor(View): return TemplateResponse(request, "author/edit_author.html", data) author = form.save() - return redirect("/author/%s" % author.id) + return redirect(f"/author/{author.id}") diff --git a/bookwyrm/views/edit_user.py b/bookwyrm/views/edit_user.py index b97f2737..4026a531 100644 --- a/bookwyrm/views/edit_user.py +++ b/bookwyrm/views/edit_user.py @@ -79,7 +79,7 @@ def save_user_form(form): # set the name to a hash extension = form.files["avatar"].name.split(".")[-1] - filename = "%s.%s" % (uuid4(), extension) + filename = f"{uuid4()}.{extension}" user.avatar.save(filename, image, save=False) user.save() return user diff --git a/bookwyrm/views/editions.py b/bookwyrm/views/editions.py index 7615c497..d044f17c 100644 --- a/bookwyrm/views/editions.py +++ b/bookwyrm/views/editions.py @@ -96,4 +96,4 @@ def switch_edition(request): readthrough.book = new_edition readthrough.save() - return redirect("/book/%d" % new_edition.id) + return redirect(f"/book/{new_edition.id}") diff --git a/bookwyrm/views/feed.py b/bookwyrm/views/feed.py index d17de8f9..98a1c836 100644 --- a/bookwyrm/views/feed.py +++ b/bookwyrm/views/feed.py @@ -42,7 +42,7 @@ class Feed(View): "tab": tab, "streams": STREAMS, "goal_form": forms.GoalForm(), - "path": "/%s" % tab["key"], + "path": f"/{tab['key']}", }, } return TemplateResponse(request, "feed/feed.html", data) diff --git a/bookwyrm/views/follow.py b/bookwyrm/views/follow.py index c3336a94..0710a70c 100644 --- a/bookwyrm/views/follow.py +++ b/bookwyrm/views/follow.py @@ -86,4 +86,4 @@ def delete_follow_request(request): return HttpResponseBadRequest() follow_request.delete() - return redirect("/user/%s" % request.user.localname) + return redirect(f"/user/{request.user.localname}") diff --git a/bookwyrm/views/helpers.py b/bookwyrm/views/helpers.py index b1e5f68c..e6db1487 100644 --- a/bookwyrm/views/helpers.py +++ b/bookwyrm/views/helpers.py @@ -113,7 +113,7 @@ def handle_remote_webfinger(query): try: user = models.User.objects.get(username__iexact=query) except models.User.DoesNotExist: - url = "https://%s/.well-known/webfinger?resource=acct:%s" % (domain, query) + url = f"https://{domain}/.well-known/webfinger?resource=acct:{query}" try: data = get_data(url) except (ConnectorException, HTTPError): diff --git a/bookwyrm/views/import_data.py b/bookwyrm/views/import_data.py index 634940a0..2232e39c 100644 --- a/bookwyrm/views/import_data.py +++ b/bookwyrm/views/import_data.py @@ -68,7 +68,7 @@ class Import(View): importer.start_import(job) - return redirect("/import/%d" % job.id) + return redirect(f"/import/{job.id}") return HttpResponseBadRequest() @@ -112,4 +112,4 @@ class ImportStatus(View): items, ) importer.start_import(job) - return redirect("/import/%d" % job.id) + return redirect(f"/import/{job.id}") diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index e255b4fa..ff01cfb2 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -71,7 +71,7 @@ def is_blocked_user_agent(request): user_agent = request.headers.get("User-Agent") if not user_agent: return False - url = re.search(r"https?://{:s}/?".format(regex.DOMAIN), user_agent) + url = re.search(rf"https?://{regex.DOMAIN}/?", user_agent) if not url: return False url = url.group() diff --git a/bookwyrm/views/list.py b/bookwyrm/views/list.py index af99e9f5..8faadb30 100644 --- a/bookwyrm/views/list.py +++ b/bookwyrm/views/list.py @@ -322,7 +322,7 @@ def add_book(request): path = reverse("list", args=[book_list.id]) params = request.GET.copy() params["updated"] = True - return redirect("{:s}?{:s}".format(path, urlencode(params))) + return redirect(f"{path}?{urlencode(params)}") @require_POST @@ -396,7 +396,7 @@ def set_book_position(request, list_item_id): def increment_order_in_reverse( book_list_id: int, start: int, end: Optional[int] = None ): - """increase the order nu,ber for every item in a list""" + """increase the order number for every item in a list""" try: book_list = models.List.objects.get(id=book_list_id) except models.List.DoesNotExist: diff --git a/bookwyrm/views/login.py b/bookwyrm/views/login.py index 97d54169..6c40e4ce 100644 --- a/bookwyrm/views/login.py +++ b/bookwyrm/views/login.py @@ -46,7 +46,7 @@ class Login(View): except models.User.DoesNotExist: # maybe it's a full username? username = localname else: - username = "%s@%s" % (localname, DOMAIN) + username = f"{localname}@{DOMAIN}" password = login_form.data["password"] # perform authentication diff --git a/bookwyrm/views/password.py b/bookwyrm/views/password.py index 6d202ce2..baf1b3ff 100644 --- a/bookwyrm/views/password.py +++ b/bookwyrm/views/password.py @@ -38,7 +38,7 @@ class PasswordResetRequest(View): # create a new reset code code = models.PasswordReset.objects.create(user=user) password_reset_email(code) - data = {"message": _("A password reset link sent to %s" % email)} + data = {"message": _(f"A password reset link sent to {email}")} return TemplateResponse(request, "password_reset_request.html", data) diff --git a/bookwyrm/views/register.py b/bookwyrm/views/register.py index 1a0615ac..1e0dd1d5 100644 --- a/bookwyrm/views/register.py +++ b/bookwyrm/views/register.py @@ -64,7 +64,7 @@ class Register(View): return TemplateResponse(request, "invite.html", data) return TemplateResponse(request, "login.html", data) - username = "%s@%s" % (localname, DOMAIN) + username = f"{localname}@{DOMAIN}" user = models.User.objects.create_user( username, email, diff --git a/bookwyrm/views/search.py b/bookwyrm/views/search.py index cdea8663..d8abdce5 100644 --- a/bookwyrm/views/search.py +++ b/bookwyrm/views/search.py @@ -63,7 +63,7 @@ class Search(View): data["results"] = paginated data["remote"] = search_remote - return TemplateResponse(request, "search/{:s}.html".format(search_type), data) + return TemplateResponse(request, f"search/{search_type}.html", data) def book_search(query, _, min_confidence, search_remote=False): diff --git a/bookwyrm/views/status.py b/bookwyrm/views/status.py index 0ead2d01..c8131f65 100644 --- a/bookwyrm/views/status.py +++ b/bookwyrm/views/status.py @@ -36,7 +36,7 @@ class CreateStatus(View): status_type = status_type[0].upper() + status_type[1:] try: - form = getattr(forms, "%sForm" % status_type)(request.POST) + form = getattr(forms, f"{status_type}Form")(request.POST) except AttributeError: return HttpResponseBadRequest() if not form.is_valid(): @@ -58,8 +58,8 @@ class CreateStatus(View): # turn the mention into a link content = re.sub( - r"%s([^@]|$)" % mention_text, - r'%s\g<1>' % (mention_user.remote_id, mention_text), + rf"{mention_text}([^@]|$)", + rf'{mention_text}\g<1>', content, ) # add reply parent to mentions @@ -182,7 +182,7 @@ def format_links(content): if url.fragment != "": link += "#" + url.fragment - formatted_content += '%s' % (potential_link, link) + formatted_content += f'{link}' except (ValidationError, UnicodeError): formatted_content += potential_link diff --git a/bookwyrm/views/wellknown.py b/bookwyrm/views/wellknown.py index c981eb69..660a4524 100644 --- a/bookwyrm/views/wellknown.py +++ b/bookwyrm/views/wellknown.py @@ -26,7 +26,7 @@ def webfinger(request): return JsonResponse( { - "subject": "acct:%s" % (user.username), + "subject": f"acct:{user.username}", "links": [ { "rel": "self", @@ -46,7 +46,7 @@ def nodeinfo_pointer(_): "links": [ { "rel": "http://nodeinfo.diaspora.software/ns/schema/2.0", - "href": "https://%s/nodeinfo/2.0" % DOMAIN, + "href": f"https://{DOMAIN}/nodeinfo/2.0", } ] } From 08f6a9765379ab96cbf8348024923ea8a24e8254 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 11:33:43 -0700 Subject: [PATCH 023/178] Python formatting --- bookwyrm/activitystreams.py | 4 +++- bookwyrm/connectors/abstract_connector.py | 4 +--- bookwyrm/connectors/inventaire.py | 10 ++-------- bookwyrm/emailing.py | 12 +++--------- bookwyrm/forms.py | 5 +---- bookwyrm/models/fields.py | 2 +- bookwyrm/models/status.py | 2 +- bookwyrm/models/user.py | 4 +--- bookwyrm/preview_images.py | 2 +- bookwyrm/settings.py | 4 +++- 10 files changed, 17 insertions(+), 32 deletions(-) diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index 13d56f84..c2b3da5e 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -20,7 +20,9 @@ class ActivityStream(RedisStore): def unread_id(self, user): """the redis key for this user's unread count for this stream""" - return "{}-unread".format(self.stream_id(user)) # pylint: disable=consider-using-f-string + return "{}-unread".format( + self.stream_id(user) + ) # pylint: disable=consider-using-f-string def get_rank(self, obj): # pylint: disable=no-self-use """statuses are sorted by date published""" diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index 902ccd6c..455241cc 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -222,9 +222,7 @@ def get_data(url, params=None, timeout=10): """wrapper for request.get""" # check if the url is blocked if models.FederatedServer.is_blocked(url): - raise ConnectorException( - f"Attempting to load data from blocked url: {url}" - ) + raise ConnectorException(f"Attempting to load data from blocked url: {url}") try: resp = requests.get( diff --git a/bookwyrm/connectors/inventaire.py b/bookwyrm/connectors/inventaire.py index b23a7d52..70455488 100644 --- a/bookwyrm/connectors/inventaire.py +++ b/bookwyrm/connectors/inventaire.py @@ -87,11 +87,7 @@ class Connector(AbstractConnector): def format_search_result(self, search_result): images = search_result.get("image") - cover = ( - f"{self.covers_url}/img/entities/{images[0]}" - if images - else None - ) + cover = f"{self.covers_url}/img/entities/{images[0]}" if images else None # a deeply messy translation of inventaire's scores confidence = float(search_result.get("_score", 0.1)) confidence = 0.1 if confidence < 150 else 0.999 @@ -131,9 +127,7 @@ class Connector(AbstractConnector): def load_edition_data(self, work_uri): """get a list of editions for a work""" - url = ( - f"{self.books_url}?action=reverse-claims&property=wdt:P629&value={work_uri}&sort=true" - ) + url = f"{self.books_url}?action=reverse-claims&property=wdt:P629&value={work_uri}&sort=true" return get_data(url) def get_edition_from_work_data(self, data): diff --git a/bookwyrm/emailing.py b/bookwyrm/emailing.py index 10db4f13..c6a197f2 100644 --- a/bookwyrm/emailing.py +++ b/bookwyrm/emailing.py @@ -48,18 +48,12 @@ def password_reset_email(reset_code): def format_email(email_name, data): """render the email templates""" - subject = ( - get_template(f"email/{email_name}/subject.html").render(data).strip() - ) + subject = get_template(f"email/{email_name}/subject.html").render(data).strip() html_content = ( - get_template(f"email/{email_name}/html_content.html") - .render(data) - .strip() + get_template(f"email/{email_name}/html_content.html").render(data).strip() ) text_content = ( - get_template(f"email/{email_name}/text_content.html") - .render(data) - .strip() + get_template(f"email/{email_name}/text_content.html").render(data).strip() ) return (subject, html_content, text_content) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index d8228b7e..23063ff7 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -260,10 +260,7 @@ class CreateInviteForm(CustomForm): ] ), "use_limit": widgets.Select( - choices=[ - (i, _(f"{i} uses")) - for i in [1, 5, 10, 25, 50, 100] - ] + choices=[(i, _(f"{i} uses")) for i in [1, 5, 10, 25, 50, 100]] + [(None, _("Unlimited"))] ), } diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py index 8555e06f..ccd669cb 100644 --- a/bookwyrm/models/fields.py +++ b/bookwyrm/models/fields.py @@ -56,7 +56,7 @@ class ActivitypubFieldMixin: activitypub_field=None, activitypub_wrapper=None, deduplication_field=False, - **kwargs + **kwargs, ): self.deduplication_field = deduplication_field if activitypub_wrapper: diff --git a/bookwyrm/models/status.py b/bookwyrm/models/status.py index 0b6e49e2..e3a3d387 100644 --- a/bookwyrm/models/status.py +++ b/bookwyrm/models/status.py @@ -181,7 +181,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel): self.replies(self), remote_id=f"{self.remote_id}/replies", collection_only=True, - **kwargs + **kwargs, ).serialize() def to_activity_dataclass(self, pure=False): # pylint: disable=arguments-differ diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index 750cfa44..ad43270c 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -198,9 +198,7 @@ class User(OrderedCollectionPageMixin, AbstractUser): def to_outbox(self, filter_type=None, **kwargs): """an ordered collection of statuses""" if filter_type: - filter_class = apps.get_model( - f"bookwyrm.{filter_type}", require_ready=True - ) + filter_class = apps.get_model(f"bookwyrm.{filter_type}", require_ready=True) if not issubclass(filter_class, Status): raise TypeError( "filter_status_class must be a subclass of models.Status" diff --git a/bookwyrm/preview_images.py b/bookwyrm/preview_images.py index c9ef4643..55af3532 100644 --- a/bookwyrm/preview_images.py +++ b/bookwyrm/preview_images.py @@ -413,7 +413,7 @@ def generate_user_preview_image_task(user_id): texts = { "text_one": user.display_name, - "text_three": "@{user.localname}@{settings.DOMAIN}" + "text_three": "@{user.localname}@{settings.DOMAIN}", } if user.avatar: diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index c6a816ed..285d2df8 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -23,7 +23,9 @@ EMAIL_HOST_USER = env("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True) EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False) -DEFAULT_FROM_EMAIL = "admin@{:s}".format(env("DOMAIN")) # pylint: disable=consider-using-f-string +DEFAULT_FROM_EMAIL = "admin@{:s}".format( + env("DOMAIN") +) # pylint: disable=consider-using-f-string # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From 0fe5cb1000fce84d5f244ae78839fcee1d85444e Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 11:56:32 -0700 Subject: [PATCH 024/178] Show label for content warning field --- .../create_status/content_warning_field.html | 12 ++++++-- .../snippets/create_status/layout.html | 28 ++++++++----------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/bookwyrm/templates/snippets/create_status/content_warning_field.html b/bookwyrm/templates/snippets/create_status/content_warning_field.html index 86b40ae2..d95e1911 100644 --- a/bookwyrm/templates/snippets/create_status/content_warning_field.html +++ b/bookwyrm/templates/snippets/create_status/content_warning_field.html @@ -1,6 +1,14 @@ {% load i18n %} -
    - +
    + {% endblock %} + {% include "snippets/create_status/content_warning_field.html" %} + + {# fields that go between the content warnings and the content field (ie, quote) #} + {% block pre_content_additions %}{% endblock %} + + +
    -
    - {% include "snippets/create_status/content_warning_field.html" %} -
    - - {# fields that go between the content warnings and the content field (ie, quote) #} - {% block pre_content_additions %}{% endblock %} - - - -
    - {% include "snippets/create_status/content_field.html" with placeholder=placeholder %} -
    + {% include "snippets/create_status/content_field.html" with placeholder=placeholder %}
    {# additional fields that go after the content block (ie, progress) #} From 19e36eb181c1593f8781bcf00be633ebcdfa04b7 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 14:05:17 -0700 Subject: [PATCH 025/178] Fixes content warning fields in modals --- .../snippets/create_status/content_warning_field.html | 6 +++--- .../snippets/create_status/content_warning_toggle.html | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/bookwyrm/templates/snippets/create_status/content_warning_field.html b/bookwyrm/templates/snippets/create_status/content_warning_field.html index d95e1911..5ed1fad1 100644 --- a/bookwyrm/templates/snippets/create_status/content_warning_field.html +++ b/bookwyrm/templates/snippets/create_status/content_warning_field.html @@ -1,11 +1,11 @@ {% load i18n %}
    @@ -14,7 +14,7 @@ name="content_warning" maxlength="255" class="input" - id="id_content_warning_{{ uuid }}" + id="id_content_warning_{{ uuid }}{{ local_uuid }}" placeholder="{% trans 'Spoilers ahead!' %}" value="{% firstof draft.content_warning parent_status.content_warning '' %}" data-cache-draft="id_content_warning_{{ book.id }}_{{ type }}" diff --git a/bookwyrm/templates/snippets/create_status/content_warning_toggle.html b/bookwyrm/templates/snippets/create_status/content_warning_toggle.html index 1c7676db..f29d47c4 100644 --- a/bookwyrm/templates/snippets/create_status/content_warning_toggle.html +++ b/bookwyrm/templates/snippets/create_status/content_warning_toggle.html @@ -5,12 +5,13 @@ type="checkbox" class="is-hidden" name="sensitive" - id="id_show_spoilers_{{ uuid }}" + id="id_show_spoilers_{{ uuid }}{{ local_uuid }}" {% if draft.content_warning or status.content_warning %}checked{% endif %} aria-hidden="true" data-cache-draft="id_sensitive_{{ book.id }}_{{ type }}{{ reply_parent.id }}" > {% trans "Include spoiler alert" as button_text %} {% firstof draft.content_warning status.content_warning as pressed %} - {% include 'snippets/toggle/toggle_button.html' with text=button_text icon="warning is-size-4" controls_text="spoilers" controls_uid=uuid focus="id_content_warning" checkbox="id_show_spoilers" class="toggle-button" pressed=pressed %} + {% firstof local_uuid '' as local_uuid %} + {% include 'snippets/toggle/toggle_button.html' with text=button_text icon="warning is-size-4" controls_text="spoilers" controls_uid=uuid|add:local_uuid focus="id_content_warning" checkbox="id_show_spoilers" class="toggle-button" pressed=pressed %}
    From 293962326c3c808e8f75c1523a94b313755479e3 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 14:32:30 -0700 Subject: [PATCH 026/178] Tick javascript hash --- bookwyrm/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index cf33c129..cdf700ec 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -13,7 +13,7 @@ VERSION = "0.0.1" PAGE_LENGTH = env("PAGE_LENGTH", 15) DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English") -JS_CACHE = "e5832a26" +JS_CACHE = "7f2343cf" # email EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend") From dda36f22c285b57a2dc467fae4cd0996433249dc Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 14:48:03 -0700 Subject: [PATCH 027/178] Fixes select all checkbox --- bookwyrm/templates/import/import_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/templates/import/import_status.html b/bookwyrm/templates/import/import_status.html index 1c425af6..1c073944 100644 --- a/bookwyrm/templates/import/import_status.html +++ b/bookwyrm/templates/import/import_status.html @@ -77,7 +77,7 @@ class="checkbox" type="checkbox" data-action="toggle-all" - data-target="failed-imports" + data-target="failed_imports" /> {% trans "Select all" %} From 01ffb8d77b1999107380e4bcd0976e69bc1e0e94 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 14:56:20 -0700 Subject: [PATCH 028/178] Fixes form submission from compose view --- bookwyrm/templates/compose.html | 2 +- bookwyrm/templates/snippets/create_status/layout.html | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/compose.html b/bookwyrm/templates/compose.html index e37ec170..3a222cf6 100644 --- a/bookwyrm/templates/compose.html +++ b/bookwyrm/templates/compose.html @@ -27,7 +27,7 @@ {% if not draft %} {% include 'snippets/create_status.html' %} {% else %} - {% include 'snippets/create_status/status.html' %} + {% include 'snippets/create_status/status.html' with no_script=True %} {% endif %}
    diff --git a/bookwyrm/templates/snippets/create_status/layout.html b/bookwyrm/templates/snippets/create_status/layout.html index d62456b8..1e10085c 100644 --- a/bookwyrm/templates/snippets/create_status/layout.html +++ b/bookwyrm/templates/snippets/create_status/layout.html @@ -14,7 +14,13 @@ reply_parent: the Status object this post will be in reply to, if applicable {% block form_open %} {# default form tag syntax, can be overriddden #} -
    + {% endblock %} {% csrf_token %} From 385da1ea430f3e2492a32b453989f2d4896dc5e9 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 15:08:02 -0700 Subject: [PATCH 029/178] Handle get request to /register path --- bookwyrm/views/register.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bookwyrm/views/register.py b/bookwyrm/views/register.py index 1a0615ac..202e2089 100644 --- a/bookwyrm/views/register.py +++ b/bookwyrm/views/register.py @@ -16,6 +16,11 @@ from bookwyrm.settings import DOMAIN class Register(View): """register a user""" + def get(self, request): # pylint: disable=unused-argument + """whether or not you're logged in, just go to the home view""" + return redirect("/") + + @sensitive_variables("password") @method_decorator(sensitive_post_parameters("password")) def post(self, request): From 246ab992ae1f4ae6fd8eee6678dbff2575af2908 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 15:10:25 -0700 Subject: [PATCH 030/178] Consistent layout for invite registration page --- bookwyrm/templates/invite.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bookwyrm/templates/invite.html b/bookwyrm/templates/invite.html index 22a3a32c..fcd379e2 100644 --- a/bookwyrm/templates/invite.html +++ b/bookwyrm/templates/invite.html @@ -5,11 +5,11 @@ {% block content %} +

    {% trans "Create an Account" %}

    {% if valid %} -

    {% trans "Create an Account" %}

    @@ -25,7 +25,7 @@
    -
    +
    {% include 'snippets/about.html' %}
    From 1be4f31a9e07ca24badd3598d0ecdf3c23220054 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 15:15:48 -0700 Subject: [PATCH 031/178] Cleans up display of login page --- bookwyrm/templates/login.html | 96 +++++++++++++++++------------------ 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/bookwyrm/templates/login.html b/bookwyrm/templates/login.html index 19c103ea..31e9cebb 100644 --- a/bookwyrm/templates/login.html +++ b/bookwyrm/templates/login.html @@ -4,69 +4,65 @@ {% block title %}{% trans "Login" %}{% endblock %} {% block content %} -
    -
    -
    -

    {% trans "Log in" %}

    - {% if login_form.non_field_errors %} -

    {{ login_form.non_field_errors }}

    - {% endif %} +

    {% trans "Log in" %}

    +
    +
    + {% if login_form.non_field_errors %} +

    {{ login_form.non_field_errors }}

    + {% endif %} - {% if show_confirmed_email %} -

    {% trans "Success! Email address confirmed." %}

    - {% endif %} - - {% csrf_token %} - {% if show_confirmed_email %}{% endif %} -
    - -
    - {{ login_form.localname }} -
    + {% if show_confirmed_email %} +

    {% trans "Success! Email address confirmed." %}

    + {% endif %} + + {% csrf_token %} + {% if show_confirmed_email %}{% endif %} +
    + +
    + {{ login_form.localname }}
    -
    - -
    - {{ login_form.password }} -
    - {% for error in login_form.password.errors %} -

    {{ error | escape }}

    - {% endfor %} +
    +
    + +
    + {{ login_form.password }}
    -
    -
    - -
    - + {% for error in login_form.password.errors %} +

    {{ error | escape }}

    + {% endfor %} +
    +
    +
    +
    - -
    + +
    +
    -
    + {% if site.allow_registration %} +
    - {% if site.allow_registration %}

    {% trans "Create an Account" %}

    {% include 'snippets/register_form.html' %}
    - {% else %} -

    {% trans "This instance is closed" %}

    -

    {% trans "Contact an administrator to get an invite" %}

    - {% endif %} +
    +
    + {% endif %} + +
    +
    + {% include 'snippets/about.html' %} + +

    + {% trans "More about this site" %} +

    -
    -
    - {% include 'snippets/about.html' %} - -

    - {% trans "More about this site" %} -

    -
    -
    {% endblock %} From 38c66b208c4e5fca6b3a7de2c121505b2847fcf0 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 18 Sep 2021 15:42:48 -0700 Subject: [PATCH 032/178] Adds customizable invite request copy --- .../migrations/0098_auto_20210918_2238.py | 27 ++++++++ bookwyrm/models/site.py | 5 +- .../templates/landing/landing_layout.html | 63 ++++++++++--------- bookwyrm/templates/settings/site.html | 7 +++ bookwyrm/views/register.py | 1 - 5 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 bookwyrm/migrations/0098_auto_20210918_2238.py diff --git a/bookwyrm/migrations/0098_auto_20210918_2238.py b/bookwyrm/migrations/0098_auto_20210918_2238.py new file mode 100644 index 00000000..09fdba31 --- /dev/null +++ b/bookwyrm/migrations/0098_auto_20210918_2238.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.4 on 2021-09-18 22:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0097_auto_20210917_1858"), + ] + + operations = [ + migrations.AddField( + model_name="sitesettings", + name="invite_request_text", + field=models.TextField( + default="If your request is approved, you will receive an email with a registration link." + ), + ), + migrations.AlterField( + model_name="sitesettings", + name="registration_closed_text", + field=models.TextField( + default='We aren\'t taking new users at this time. You can find an open instance at joinbookwyrm.com/instances.' + ), + ), + ] diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index ee69a507..4646c0c7 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -24,7 +24,10 @@ class SiteSettings(models.Model): # about page registration_closed_text = models.TextField( - default="Contact an administrator to get an invite" + default='We aren\'t taking new users at this time. You can find an open instance at joinbookwyrm.com/instances.' + ) + invite_request_text = models.TextField( + default="If your request is approved, you will receive an email with a registration link." ) code_of_conduct = models.TextField(default="Add a code of conduct here.") privacy_policy = models.TextField(default="Add a privacy policy here.") diff --git a/bookwyrm/templates/landing/landing_layout.html b/bookwyrm/templates/landing/landing_layout.html index 946482cb..7397d162 100644 --- a/bookwyrm/templates/landing/landing_layout.html +++ b/bookwyrm/templates/landing/landing_layout.html @@ -40,38 +40,41 @@
    {% if not request.user.is_authenticated %}
    +

    + {% if site.allow_registration %} + {% blocktrans with name=site.name %}Join {{ name }}{% endblocktrans %} + {% elif site.allow_invite_requests %} + {% trans "Request an Invitation" %} + {% else %} + {% blocktrans with name=site.name%}{{ name}} registration is closed{% endblocktrans %} + {% endif %} +

    + {% if site.allow_registration %} -

    {% blocktrans with name=site.name %}Join {{ name }}{% endblocktrans %}

    -
    - {% include 'snippets/register_form.html' %} -
    - +
    + {% include 'snippets/register_form.html' %} +
    + {% elif site.allow_invite_requests %} + {% if request_received %} +

    + {% trans "Thank you! Your request has been received." %} +

    + {% else %} +

    {{ site.invite_request_text }}

    +

    + {% csrf_token %} +
    + + + {% for error in request_form.email.errors %} +

    {{ error|escape }}

    + {% endfor %} +
    + +
    + {% endif %} {% else %} - -

    {% trans "This instance is closed" %}

    -

    {{ site.registration_closed_text|safe}}

    - - {% if site.allow_invite_requests %} - {% if request_received %} -

    - {% trans "Thank you! Your request has been received." %} -

    - {% else %} -

    {% trans "Request an Invitation" %}

    -
    - {% csrf_token %} -
    - - - {% for error in request_form.email.errors %} -

    {{ error|escape }}

    - {% endfor %} -
    - -
    - {% endif %} - {% endif %} - +

    {{ site.registration_closed_text|safe}}

    {% endif %}
    {% else %} diff --git a/bookwyrm/templates/settings/site.html b/bookwyrm/templates/settings/site.html index 945e5749..50895485 100644 --- a/bookwyrm/templates/settings/site.html +++ b/bookwyrm/templates/settings/site.html @@ -107,6 +107,13 @@ {{ site_form.registration_closed_text }}
    +
    + + {{ site_form.invite_request_text }} + {% for error in site_form.invite_request_text.errors %} +

    {{ error|escape }}

    + {% endfor %} +