diff --git a/.env.example b/.env.example index ca6f65bb..bb2d677e 100644 --- a/.env.example +++ b/.env.example @@ -41,7 +41,7 @@ REDIS_BROKER_PASSWORD=redispassword123 # Monitoring for celery FLOWER_PORT=8888 -FLOWER_USER=mouse +FLOWER_USER=admin FLOWER_PASSWORD=changeme # Email config @@ -89,3 +89,22 @@ PREVIEW_TEXT_COLOR=#363636 PREVIEW_IMG_WIDTH=1200 PREVIEW_IMG_HEIGHT=630 PREVIEW_DEFAULT_COVER_COLOR=#002549 + +# Below are example keys if you want to enable automatically +# sending telemetry to an OTLP-compatible service. Many of +# the main monitoring apps have OLTP collectors, including +# NewRelic, DataDog, and Honeycomb.io - consult their +# documentation for setup instructions, and what exactly to +# put below! +# +# Service name is an arbitrary tag that is attached to any +# data sent, used to distinguish different sources. Useful +# for sending prod and dev metrics to the same place and +# keeping them separate, for instance! + +# API endpoint for your provider +OTEL_EXPORTER_OTLP_ENDPOINT= +# Any headers required, usually authentication info +OTEL_EXPORTER_OTLP_HEADERS= +# Service name to identify your app +OTEL_SERVICE_NAME= diff --git a/.github/workflows/lint-frontend.yaml b/.github/workflows/lint-frontend.yaml index 54cac04d..ed106d6a 100644 --- a/.github/workflows/lint-frontend.yaml +++ b/.github/workflows/lint-frontend.yaml @@ -1,5 +1,5 @@ # @url https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions -name: Lint Frontend +name: Lint Frontend (run `./bw-dev stylelint` to fix css errors) on: push: @@ -8,7 +8,7 @@ on: - '.github/workflows/**' - 'static/**' - '.eslintrc' - - '.stylelintrc' + - '.stylelintrc.js' pull_request: branches: [ main, ci, frontend ] @@ -22,17 +22,16 @@ jobs: - uses: actions/checkout@v2 - name: Install modules - run: yarn + run: npm install stylelint stylelint-config-recommended stylelint-config-standard stylelint-order eslint # See .stylelintignore for files that are not linted. - name: Run stylelint run: > - yarn stylelint bookwyrm/static/**/*.css \ - --report-needless-disables \ - --report-invalid-scope-disables + npx stylelint bookwyrm/static/css/*.scss bookwyrm/static/css/bookwyrm/**/*.scss \ + --config dev-tools/.stylelintrc.js # See .eslintignore for files that are not linted. - name: Run ESLint run: > - yarn eslint bookwyrm/static \ + npx eslint bookwyrm/static \ --ext .js,.jsx,.ts,.tsx diff --git a/.github/workflows/lint-global.yaml b/.github/workflows/lint-global.yaml deleted file mode 100644 index 81893970..00000000 --- a/.github/workflows/lint-global.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# @url https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions -name: Lint project globally - -on: - push: - branches: [ main, ci ] - pull_request: - branches: [ main, ci ] - -jobs: - lint: - name: Lint with EditorConfig. - runs-on: ubuntu-20.04 - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - - name: EditorConfig - uses: greut/eclint-action@v0 diff --git a/.github/workflows/prettier.yaml b/.github/workflows/prettier.yaml index 80696060..c4a031db 100644 --- a/.github/workflows/prettier.yaml +++ b/.github/workflows/prettier.yaml @@ -17,8 +17,7 @@ jobs: - uses: actions/checkout@v2 - name: Install modules - run: npm install . + run: npm install prettier - # See .stylelintignore for files that are not linted. - name: Run Prettier run: npx prettier --check bookwyrm/static/js/*.js diff --git a/.gitignore b/.gitignore index e5582694..ec2a08f8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ # BookWyrm .env /images/ +bookwyrm/static/css/bookwyrm.css +bookwyrm/static/css/themes/ +!bookwyrm/static/css/themes/bookwyrm-*.scss # Testing .coverage @@ -24,7 +27,9 @@ .idea #Node tools -/node_modules/ +node_modules/ +package-lock.json +yarn.lock #nginx nginx/default.conf diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..9fa808ee --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +**/vendor/* diff --git a/README.md b/README.md index 161f91b9..bd7344df 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Social reading and reviewing, decentralized with ActivityPub - [Set up Bookwyrm](#set-up-bookwyrm) ## Joining BookWyrm -BookWyrm is still a young piece of software, and isn't at the level of stability and feature-richness that you'd find in a production-ready application. But it does what it says on the box! If you'd like to join an instance, you can check out the [instances](https://docs.joinbookwyrm.com/instances.html) list. +BookWyrm is still a young piece of software, and isn't at the level of stability and feature-richness that you'd find in a production-ready application. But it does what it says on the box! If you'd like to join an instance, you can check out the [instances](https://joinbookwyrm.com/instances/) list. You can request an invite by entering your email address at https://bookwyrm.social. diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 15ca5a93..6bee25f6 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -227,7 +227,7 @@ def set_related_field( model_field = getattr(model, related_field_name) if hasattr(model_field, "activitypub_field"): setattr(activity, getattr(model_field, "activitypub_field"), instance.remote_id) - item = activity.to_model() + item = activity.to_model(model=model) # if the related field isn't serialized (attachments on Status), then # we have to set it post-creation @@ -298,6 +298,7 @@ class Link(ActivityObject): mediaType: str = None id: str = None attributedTo: str = None + availability: str = None type: str = "Link" def serialize(self, **kwargs): diff --git a/bookwyrm/activitypub/book.py b/bookwyrm/activitypub/book.py index 2238e3a8..e6a01b35 100644 --- a/bookwyrm/activitypub/book.py +++ b/bookwyrm/activitypub/book.py @@ -16,6 +16,9 @@ class BookData(ActivityObject): librarythingKey: str = None goodreadsKey: str = None bnfId: str = None + viaf: str = None + wikidata: str = None + asin: str = None lastEditedBy: str = None links: List[str] = field(default_factory=lambda: []) fileLinks: List[str] = field(default_factory=lambda: []) @@ -27,8 +30,8 @@ class Book(BookData): """serializes an edition or work, abstract""" title: str - sortTitle: str = "" - subtitle: str = "" + sortTitle: str = None + subtitle: str = None description: str = "" languages: List[str] = field(default_factory=lambda: []) series: str = "" @@ -53,7 +56,6 @@ class Edition(Book): isbn10: str = "" isbn13: str = "" oclcNumber: str = "" - asin: str = "" pages: int = None physicalFormat: str = "" physicalFormatDetail: str = "" diff --git a/bookwyrm/activitypub/person.py b/bookwyrm/activitypub/person.py index 576e7f9a..61c15a57 100644 --- a/bookwyrm/activitypub/person.py +++ b/bookwyrm/activitypub/person.py @@ -39,4 +39,5 @@ class Person(ActivityObject): bookwyrmUser: bool = False manuallyApprovesFollowers: str = False discoverable: str = False + hideFollows: str = False type: str = "Person" diff --git a/bookwyrm/activitypub/verbs.py b/bookwyrm/activitypub/verbs.py index b32b0413..36898bc7 100644 --- a/bookwyrm/activitypub/verbs.py +++ b/bookwyrm/activitypub/verbs.py @@ -38,7 +38,7 @@ class Create(Verb): class Delete(Verb): """Create activity""" - to: List[str] + to: List[str] = field(default_factory=lambda: []) cc: List[str] = field(default_factory=lambda: []) type: str = "Delete" @@ -137,8 +137,8 @@ class Accept(Verb): type: str = "Accept" def action(self): - """find and remove the activity object""" - obj = self.object.to_model(save=False, allow_create=False) + """accept a request""" + obj = self.object.to_model(save=False, allow_create=True) obj.accept() @@ -150,7 +150,7 @@ class Reject(Verb): type: str = "Reject" def action(self): - """find and remove the activity object""" + """reject a follow request""" obj = self.object.to_model(save=False, allow_create=False) obj.reject() diff --git a/bookwyrm/apps.py b/bookwyrm/apps.py new file mode 100644 index 00000000..786f86e1 --- /dev/null +++ b/bookwyrm/apps.py @@ -0,0 +1,54 @@ +"""Do further startup configuration and initialization""" +import os +import urllib +import logging + +from django.apps import AppConfig + +from bookwyrm import settings + +logger = logging.getLogger(__name__) + + +def download_file(url, destination): + """Downloads a file to the given path""" + try: + # Ensure our destination directory exists + os.makedirs(os.path.dirname(destination)) + with urllib.request.urlopen(url) as stream: + with open(destination, "b+w") as outfile: + outfile.write(stream.read()) + except (urllib.error.HTTPError, urllib.error.URLError): + logger.info("Failed to download file %s", url) + except OSError: + logger.info("Couldn't open font file %s for writing", destination) + except: # pylint: disable=bare-except + logger.info("Unknown error in file download") + + +class BookwyrmConfig(AppConfig): + """Handles additional configuration""" + + name = "bookwyrm" + verbose_name = "BookWyrm" + + # pylint: disable=no-self-use + def ready(self): + """set up OTLP and preview image files, if desired""" + if settings.OTEL_EXPORTER_OTLP_ENDPOINT: + # pylint: disable=import-outside-toplevel + from bookwyrm.telemetry import open_telemetry + + open_telemetry.instrumentDjango() + + if settings.ENABLE_PREVIEW_IMAGES and settings.FONTS: + # Download any fonts that we don't have yet + logger.debug("Downloading fonts..") + for name, config in settings.FONTS.items(): + font_path = os.path.join( + settings.FONT_DIR, config["directory"], config["filename"] + ) + + if "url" in config and not os.path.exists(font_path): + logger.info("Just a sec, downloading %s", name) + download_file(config["url"], font_path) diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index 5ed57df1..56e27388 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -1,7 +1,11 @@ """ functionality outline for a book data connector """ from abc import ABC, abstractmethod +import imghdr +import ipaddress import logging +from urllib.parse import urlparse +from django.core.files.base import ContentFile from django.db import transaction import requests from requests.exceptions import RequestException @@ -127,7 +131,7 @@ class AbstractConnector(AbstractMinimalConnector): try: work_data = self.get_work_from_edition_data(data) except (KeyError, ConnectorException) as err: - logger.exception(err) + logger.info(err) work_data = data if not work_data or not edition_data: @@ -248,6 +252,8 @@ def dict_from_mappings(data, mappings): def get_data(url, params=None, timeout=10): """wrapper for request.get""" # check if the url is blocked + raise_not_valid_url(url) + if models.FederatedServer.is_blocked(url): raise ConnectorException(f"Attempting to load data from blocked url: {url}") @@ -264,7 +270,7 @@ def get_data(url, params=None, timeout=10): timeout=timeout, ) except RequestException as err: - logger.exception(err) + logger.info(err) raise ConnectorException(err) if not resp.ok: @@ -272,7 +278,7 @@ def get_data(url, params=None, timeout=10): try: data = resp.json() except ValueError as err: - logger.exception(err) + logger.info(err) raise ConnectorException(err) return data @@ -280,6 +286,7 @@ def get_data(url, params=None, timeout=10): def get_image(url, timeout=10): """wrapper for requesting an image""" + raise_not_valid_url(url) try: resp = requests.get( url, @@ -289,11 +296,33 @@ def get_image(url, timeout=10): timeout=timeout, ) except RequestException as err: - logger.exception(err) - return None + logger.info(err) + return None, None + if not resp.ok: - return None - return resp + return None, None + + image_content = ContentFile(resp.content) + extension = imghdr.what(None, image_content.read()) + if not extension: + logger.info("File requested was not an image: %s", url) + return None, None + + return image_content, extension + + +def raise_not_valid_url(url): + """do some basic reality checks on the url""" + parsed = urlparse(url) + if not parsed.scheme in ["http", "https"]: + raise ConnectorException("Invalid scheme: ", url) + + try: + ipaddress.ip_address(parsed.netloc) + raise ConnectorException("Provided url is an IP address: ", url) + except ValueError: + # it's not an IP address, which is good + pass class Mapping: diff --git a/bookwyrm/connectors/connector_manager.py b/bookwyrm/connectors/connector_manager.py index 3bdd5cb4..14bb702c 100644 --- a/bookwyrm/connectors/connector_manager.py +++ b/bookwyrm/connectors/connector_manager.py @@ -39,7 +39,7 @@ def search(query, min_confidence=0.1, return_first=False): try: result_set = connector.isbn_search(isbn) except Exception as err: # pylint: disable=broad-except - logger.exception(err) + logger.info(err) # if this fails, we can still try regular search # if no isbn search results, we fallback to generic search @@ -48,7 +48,7 @@ def search(query, min_confidence=0.1, return_first=False): result_set = connector.search(query, min_confidence=min_confidence) except Exception as err: # pylint: disable=broad-except # we don't want *any* error to crash the whole search page - logger.exception(err) + logger.info(err) continue if return_first and result_set: diff --git a/bookwyrm/connectors/openlibrary.py b/bookwyrm/connectors/openlibrary.py index c15277f8..118222a1 100644 --- a/bookwyrm/connectors/openlibrary.py +++ b/bookwyrm/connectors/openlibrary.py @@ -68,7 +68,30 @@ class Connector(AbstractConnector): Mapping("born", remote_field="birth_date"), Mapping("died", remote_field="death_date"), Mapping("bio", formatter=get_description), - Mapping("isni", remote_field="remote_ids", formatter=get_isni), + Mapping( + "isni", + remote_field="remote_ids", + formatter=lambda b: get_dict_field(b, "isni"), + ), + Mapping( + "asin", + remote_field="remote_ids", + formatter=lambda b: get_dict_field(b, "amazon"), + ), + Mapping( + "viaf", + remote_field="remote_ids", + formatter=lambda b: get_dict_field(b, "viaf"), + ), + Mapping( + "wikidata", + remote_field="remote_ids", + formatter=lambda b: get_dict_field(b, "wikidata"), + ), + Mapping( + "wikipedia_link", remote_field="links", formatter=get_wikipedia_link + ), + Mapping("inventaire_id", remote_field="links", formatter=get_inventaire_id), ] def get_book_data(self, remote_id): @@ -227,11 +250,38 @@ def get_languages(language_blob): return langs -def get_isni(remote_ids_blob): +def get_dict_field(blob, field_name): """extract the isni from the remote id data for the author""" - if not remote_ids_blob or not isinstance(remote_ids_blob, dict): + if not blob or not isinstance(blob, dict): return None - return remote_ids_blob.get("isni") + return blob.get(field_name) + + +def get_wikipedia_link(links): + """extract wikipedia links""" + if not isinstance(links, list): + return None + + for link in links: + if not isinstance(link, dict): + continue + if link.get("title") == "wikipedia": + return link.get("url") + return None + + +def get_inventaire_id(links): + """extract and format inventaire ids""" + if not isinstance(links, list): + return None + + for link in links: + if not isinstance(link, dict): + continue + if link.get("title") == "inventaire.io": + iv_link = link.get("url") + return iv_link.split("/")[-1] + return None def pick_default_edition(options): diff --git a/bookwyrm/context_processors.py b/bookwyrm/context_processors.py index 309e84ed..0047bfce 100644 --- a/bookwyrm/context_processors.py +++ b/bookwyrm/context_processors.py @@ -8,8 +8,20 @@ def site_settings(request): # pylint: disable=unused-argument if not request.is_secure(): request_protocol = "http://" + site = models.SiteSettings.objects.get() + theme = "css/themes/bookwyrm-light.scss" + if ( + hasattr(request, "user") + and request.user.is_authenticated + and request.user.theme + ): + theme = request.user.theme.path + elif site.default_theme: + theme = site.default_theme.path + return { - "site": models.SiteSettings.objects.get(), + "site": site, + "site_theme": theme, "active_announcements": models.Announcement.active_announcements(), "thumbnail_generation_enabled": settings.ENABLE_THUMBNAIL_GENERATION, "media_full_url": settings.MEDIA_FULL_URL, diff --git a/bookwyrm/emailing.py b/bookwyrm/emailing.py index efef1263..80aca071 100644 --- a/bookwyrm/emailing.py +++ b/bookwyrm/emailing.py @@ -48,7 +48,9 @@ def moderation_report_email(report): data["reportee"] = report.user.localname or report.user.username data["report_link"] = report.remote_id - for admin in models.User.objects.filter(groups__name__in=["admin", "moderator"]): + for admin in models.User.objects.filter( + groups__name__in=["admin", "moderator"] + ).distinct(): data["user"] = admin.display_name send_email.delay(admin.email, *format_email("moderation_report", data)) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py deleted file mode 100644 index e442dbf4..00000000 --- a/bookwyrm/forms.py +++ /dev/null @@ -1,516 +0,0 @@ -""" using django model forms """ -import datetime -from collections import defaultdict - -from django import forms -from django.forms import ModelForm, PasswordInput, widgets, ChoiceField -from django.forms.widgets import Textarea -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ - -from bookwyrm import models -from bookwyrm.models.fields import ClearableFileInputWithWarning -from bookwyrm.models.user import FeedFilterChoices - - -class CustomForm(ModelForm): - """add css classes to the forms""" - - def __init__(self, *args, **kwargs): - css_classes = defaultdict(lambda: "") - css_classes["text"] = "input" - css_classes["password"] = "input" - css_classes["email"] = "input" - css_classes["number"] = "input" - css_classes["checkbox"] = "checkbox" - css_classes["textarea"] = "textarea" - # pylint: disable=super-with-arguments - super(CustomForm, self).__init__(*args, **kwargs) - for visible in self.visible_fields(): - if hasattr(visible.field.widget, "input_type"): - input_type = visible.field.widget.input_type - if isinstance(visible.field.widget, Textarea): - input_type = "textarea" - visible.field.widget.attrs["rows"] = 5 - visible.field.widget.attrs["class"] = css_classes[input_type] - - -# pylint: disable=missing-class-docstring -class LoginForm(CustomForm): - class Meta: - model = models.User - fields = ["localname", "password"] - help_texts = {f: None for f in fields} - widgets = { - "password": PasswordInput(), - } - - -class RegisterForm(CustomForm): - class Meta: - model = models.User - fields = ["localname", "email", "password"] - help_texts = {f: None for f in fields} - widgets = {"password": PasswordInput()} - - -class RatingForm(CustomForm): - class Meta: - model = models.ReviewRating - fields = ["user", "book", "rating", "privacy"] - - -class ReviewForm(CustomForm): - class Meta: - model = models.Review - fields = [ - "user", - "book", - "name", - "content", - "rating", - "content_warning", - "sensitive", - "privacy", - ] - - -class CommentForm(CustomForm): - class Meta: - model = models.Comment - fields = [ - "user", - "book", - "content", - "content_warning", - "sensitive", - "privacy", - "progress", - "progress_mode", - "reading_status", - ] - - -class QuotationForm(CustomForm): - class Meta: - model = models.Quotation - fields = [ - "user", - "book", - "quote", - "content", - "content_warning", - "sensitive", - "privacy", - "position", - "position_mode", - ] - - -class ReplyForm(CustomForm): - class Meta: - model = models.Status - fields = [ - "user", - "content", - "content_warning", - "sensitive", - "reply_parent", - "privacy", - ] - - -class StatusForm(CustomForm): - class Meta: - model = models.Status - fields = ["user", "content", "content_warning", "sensitive", "privacy"] - - -class DirectForm(CustomForm): - class Meta: - model = models.Status - fields = ["user", "content", "content_warning", "sensitive", "privacy"] - - -class EditUserForm(CustomForm): - class Meta: - model = models.User - fields = [ - "avatar", - "name", - "email", - "summary", - "show_goal", - "show_suggested_users", - "manually_approves_followers", - "default_post_privacy", - "discoverable", - "preferred_timezone", - "preferred_language", - ] - help_texts = {f: None for f in fields} - widgets = { - "avatar": ClearableFileInputWithWarning( - attrs={"aria-describedby": "desc_avatar"} - ), - "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), - "summary": forms.Textarea(attrs={"aria-describedby": "desc_summary"}), - "email": forms.EmailInput(attrs={"aria-describedby": "desc_email"}), - "discoverable": forms.CheckboxInput( - attrs={"aria-describedby": "desc_discoverable"} - ), - } - - -class LimitedEditUserForm(CustomForm): - class Meta: - model = models.User - fields = [ - "avatar", - "name", - "summary", - "manually_approves_followers", - "discoverable", - ] - help_texts = {f: None for f in fields} - widgets = { - "avatar": ClearableFileInputWithWarning( - attrs={"aria-describedby": "desc_avatar"} - ), - "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), - "summary": forms.Textarea(attrs={"aria-describedby": "desc_summary"}), - "discoverable": forms.CheckboxInput( - attrs={"aria-describedby": "desc_discoverable"} - ), - } - - -class DeleteUserForm(CustomForm): - class Meta: - model = models.User - fields = ["password"] - - -class UserGroupForm(CustomForm): - class Meta: - model = models.User - fields = ["groups"] - - -class FeedStatusTypesForm(CustomForm): - class Meta: - model = models.User - fields = ["feed_status_types"] - help_texts = {f: None for f in fields} - widgets = { - "feed_status_types": widgets.CheckboxSelectMultiple( - choices=FeedFilterChoices, - ), - } - - -class CoverForm(CustomForm): - class Meta: - model = models.Book - fields = ["cover"] - help_texts = {f: None for f in fields} - - -class LinkDomainForm(CustomForm): - class Meta: - model = models.LinkDomain - fields = ["name"] - - -class FileLinkForm(CustomForm): - class Meta: - model = models.FileLink - fields = ["url", "filetype", "availability", "book", "added_by"] - - -class EditionForm(CustomForm): - class Meta: - model = models.Edition - exclude = [ - "remote_id", - "origin_id", - "created_date", - "updated_date", - "edition_rank", - "authors", - "parent_work", - "shelves", - "connector", - "search_vector", - "links", - "file_links", - ] - widgets = { - "title": forms.TextInput(attrs={"aria-describedby": "desc_title"}), - "subtitle": forms.TextInput(attrs={"aria-describedby": "desc_subtitle"}), - "description": forms.Textarea( - attrs={"aria-describedby": "desc_description"} - ), - "series": forms.TextInput(attrs={"aria-describedby": "desc_series"}), - "series_number": forms.TextInput( - attrs={"aria-describedby": "desc_series_number"} - ), - "languages": forms.TextInput( - attrs={"aria-describedby": "desc_languages_help desc_languages"} - ), - "publishers": forms.TextInput( - attrs={"aria-describedby": "desc_publishers_help desc_publishers"} - ), - "first_published_date": forms.SelectDateWidget( - attrs={"aria-describedby": "desc_first_published_date"} - ), - "published_date": forms.SelectDateWidget( - attrs={"aria-describedby": "desc_published_date"} - ), - "cover": ClearableFileInputWithWarning( - attrs={"aria-describedby": "desc_cover"} - ), - "physical_format": forms.Select( - attrs={"aria-describedby": "desc_physical_format"} - ), - "physical_format_detail": forms.TextInput( - attrs={"aria-describedby": "desc_physical_format_detail"} - ), - "pages": forms.NumberInput(attrs={"aria-describedby": "desc_pages"}), - "isbn_13": forms.TextInput(attrs={"aria-describedby": "desc_isbn_13"}), - "isbn_10": forms.TextInput(attrs={"aria-describedby": "desc_isbn_10"}), - "openlibrary_key": forms.TextInput( - attrs={"aria-describedby": "desc_openlibrary_key"} - ), - "inventaire_id": forms.TextInput( - attrs={"aria-describedby": "desc_inventaire_id"} - ), - "oclc_number": forms.TextInput( - attrs={"aria-describedby": "desc_oclc_number"} - ), - "ASIN": forms.TextInput(attrs={"aria-describedby": "desc_ASIN"}), - } - - -class AuthorForm(CustomForm): - class Meta: - model = models.Author - fields = [ - "last_edited_by", - "name", - "aliases", - "bio", - "wikipedia_link", - "born", - "died", - "openlibrary_key", - "inventaire_id", - "librarything_key", - "goodreads_key", - "isni", - ] - widgets = { - "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), - "aliases": forms.TextInput(attrs={"aria-describedby": "desc_aliases"}), - "bio": forms.Textarea(attrs={"aria-describedby": "desc_bio"}), - "wikipedia_link": forms.TextInput( - attrs={"aria-describedby": "desc_wikipedia_link"} - ), - "born": forms.SelectDateWidget(attrs={"aria-describedby": "desc_born"}), - "died": forms.SelectDateWidget(attrs={"aria-describedby": "desc_died"}), - "oepnlibrary_key": forms.TextInput( - attrs={"aria-describedby": "desc_oepnlibrary_key"} - ), - "inventaire_id": forms.TextInput( - attrs={"aria-describedby": "desc_inventaire_id"} - ), - "librarything_key": forms.TextInput( - attrs={"aria-describedby": "desc_librarything_key"} - ), - "goodreads_key": forms.TextInput( - attrs={"aria-describedby": "desc_goodreads_key"} - ), - } - - -class ImportForm(forms.Form): - csv_file = forms.FileField() - - -class ExpiryWidget(widgets.Select): - def value_from_datadict(self, data, files, name): - """human-readable exiration time buckets""" - selected_string = super().value_from_datadict(data, files, name) - - if selected_string == "day": - interval = datetime.timedelta(days=1) - elif selected_string == "week": - interval = datetime.timedelta(days=7) - elif selected_string == "month": - interval = datetime.timedelta(days=31) # Close enough? - elif selected_string == "forever": - return None - else: - return selected_string # This will raise - - return timezone.now() + interval - - -class InviteRequestForm(CustomForm): - def clean(self): - """make sure the email isn't in use by a registered user""" - cleaned_data = super().clean() - email = cleaned_data.get("email") - if email and models.User.objects.filter(email=email).exists(): - self.add_error("email", _("A user with this email already exists.")) - - class Meta: - model = models.InviteRequest - fields = ["email"] - - -class CreateInviteForm(CustomForm): - class Meta: - model = models.SiteInvite - exclude = ["code", "user", "times_used", "invitees"] - widgets = { - "expiry": ExpiryWidget( - choices=[ - ("day", _("One Day")), - ("week", _("One Week")), - ("month", _("One Month")), - ("forever", _("Does Not Expire")), - ] - ), - "use_limit": widgets.Select( - choices=[(i, _(f"{i} uses")) for i in [1, 5, 10, 25, 50, 100]] - + [(None, _("Unlimited"))] - ), - } - - -class ShelfForm(CustomForm): - class Meta: - model = models.Shelf - fields = ["user", "name", "privacy", "description"] - - -class GoalForm(CustomForm): - class Meta: - model = models.AnnualGoal - fields = ["user", "year", "goal", "privacy"] - - -class SiteForm(CustomForm): - class Meta: - model = models.SiteSettings - exclude = [] - widgets = { - "instance_short_description": forms.TextInput( - attrs={"aria-describedby": "desc_instance_short_description"} - ), - "require_confirm_email": forms.CheckboxInput( - attrs={"aria-describedby": "desc_require_confirm_email"} - ), - "invite_request_text": forms.Textarea( - attrs={"aria-describedby": "desc_invite_request_text"} - ), - } - - -class AnnouncementForm(CustomForm): - class Meta: - model = models.Announcement - exclude = ["remote_id"] - widgets = { - "preview": forms.TextInput(attrs={"aria-describedby": "desc_preview"}), - "content": forms.Textarea(attrs={"aria-describedby": "desc_content"}), - "event_date": forms.SelectDateWidget( - attrs={"aria-describedby": "desc_event_date"} - ), - "start_date": forms.SelectDateWidget( - attrs={"aria-describedby": "desc_start_date"} - ), - "end_date": forms.SelectDateWidget( - attrs={"aria-describedby": "desc_end_date"} - ), - "active": forms.CheckboxInput(attrs={"aria-describedby": "desc_active"}), - } - - -class ListForm(CustomForm): - class Meta: - model = models.List - fields = ["user", "name", "description", "curation", "privacy", "group"] - - -class ListItemForm(CustomForm): - class Meta: - model = models.ListItem - fields = ["user", "book", "book_list", "notes"] - - -class GroupForm(CustomForm): - class Meta: - model = models.Group - fields = ["user", "privacy", "name", "description"] - - -class ReportForm(CustomForm): - class Meta: - model = models.Report - fields = ["user", "reporter", "statuses", "links", "note"] - - -class EmailBlocklistForm(CustomForm): - class Meta: - model = models.EmailBlocklist - fields = ["domain"] - widgets = { - "avatar": forms.TextInput(attrs={"aria-describedby": "desc_domain"}), - } - - -class IPBlocklistForm(CustomForm): - class Meta: - model = models.IPBlocklist - fields = ["address"] - - -class ServerForm(CustomForm): - class Meta: - model = models.FederatedServer - exclude = ["remote_id"] - - -class SortListForm(forms.Form): - sort_by = ChoiceField( - choices=( - ("order", _("List Order")), - ("title", _("Book Title")), - ("rating", _("Rating")), - ), - label=_("Sort By"), - ) - direction = ChoiceField( - choices=( - ("ascending", _("Ascending")), - ("descending", _("Descending")), - ), - ) - - -class ReadThroughForm(CustomForm): - def clean(self): - """make sure the email isn't in use by a registered user""" - cleaned_data = super().clean() - start_date = cleaned_data.get("start_date") - finish_date = cleaned_data.get("finish_date") - if start_date and finish_date and start_date > finish_date: - self.add_error( - "finish_date", _("Reading finish date cannot be before start date.") - ) - - class Meta: - model = models.ReadThrough - fields = ["user", "book", "start_date", "finish_date"] diff --git a/bookwyrm/forms/__init__.py b/bookwyrm/forms/__init__.py new file mode 100644 index 00000000..07575293 --- /dev/null +++ b/bookwyrm/forms/__init__.py @@ -0,0 +1,12 @@ +""" make forms available to the app """ +# site admin +from .admin import * +from .author import * +from .books import * +from .edit_user import * +from .forms import * +from .groups import * +from .landing import * +from .links import * +from .lists import * +from .status import * diff --git a/bookwyrm/forms/admin.py b/bookwyrm/forms/admin.py new file mode 100644 index 00000000..4141327d --- /dev/null +++ b/bookwyrm/forms/admin.py @@ -0,0 +1,141 @@ +""" using django model forms """ +import datetime + +from django import forms +from django.forms import widgets +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from django_celery_beat.models import IntervalSchedule + +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class ExpiryWidget(widgets.Select): + def value_from_datadict(self, data, files, name): + """human-readable exiration time buckets""" + selected_string = super().value_from_datadict(data, files, name) + + if selected_string == "day": + interval = datetime.timedelta(days=1) + elif selected_string == "week": + interval = datetime.timedelta(days=7) + elif selected_string == "month": + interval = datetime.timedelta(days=31) # Close enough? + elif selected_string == "forever": + return None + else: + return selected_string # This will raise + + return timezone.now() + interval + + +class CreateInviteForm(CustomForm): + class Meta: + model = models.SiteInvite + exclude = ["code", "user", "times_used", "invitees"] + widgets = { + "expiry": ExpiryWidget( + choices=[ + ("day", _("One Day")), + ("week", _("One Week")), + ("month", _("One Month")), + ("forever", _("Does Not Expire")), + ] + ), + "use_limit": widgets.Select( + choices=[(i, _(f"{i} uses")) for i in [1, 5, 10, 25, 50, 100]] + + [(None, _("Unlimited"))] + ), + } + + +class SiteForm(CustomForm): + class Meta: + model = models.SiteSettings + exclude = ["admin_code", "install_mode"] + widgets = { + "instance_short_description": forms.TextInput( + attrs={"aria-describedby": "desc_instance_short_description"} + ), + "require_confirm_email": forms.CheckboxInput( + attrs={"aria-describedby": "desc_require_confirm_email"} + ), + "invite_request_text": forms.Textarea( + attrs={"aria-describedby": "desc_invite_request_text"} + ), + } + + +class ThemeForm(CustomForm): + class Meta: + model = models.Theme + fields = ["name", "path"] + widgets = { + "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), + "path": forms.TextInput( + attrs={ + "aria-describedby": "desc_path", + "placeholder": "css/themes/theme-name.scss", + } + ), + } + + +class AnnouncementForm(CustomForm): + class Meta: + model = models.Announcement + exclude = ["remote_id"] + widgets = { + "preview": forms.TextInput(attrs={"aria-describedby": "desc_preview"}), + "content": forms.Textarea(attrs={"aria-describedby": "desc_content"}), + "event_date": forms.SelectDateWidget( + attrs={"aria-describedby": "desc_event_date"} + ), + "start_date": forms.SelectDateWidget( + attrs={"aria-describedby": "desc_start_date"} + ), + "end_date": forms.SelectDateWidget( + attrs={"aria-describedby": "desc_end_date"} + ), + "active": forms.CheckboxInput(attrs={"aria-describedby": "desc_active"}), + } + + +class EmailBlocklistForm(CustomForm): + class Meta: + model = models.EmailBlocklist + fields = ["domain"] + widgets = { + "avatar": forms.TextInput(attrs={"aria-describedby": "desc_domain"}), + } + + +class IPBlocklistForm(CustomForm): + class Meta: + model = models.IPBlocklist + fields = ["address"] + + +class ServerForm(CustomForm): + class Meta: + model = models.FederatedServer + exclude = ["remote_id"] + + +class AutoModRuleForm(CustomForm): + class Meta: + model = models.AutoMod + fields = ["string_match", "flag_users", "flag_statuses", "created_by"] + + +class IntervalScheduleForm(CustomForm): + class Meta: + model = IntervalSchedule + fields = ["every", "period"] + + widgets = { + "every": forms.NumberInput(attrs={"aria-describedby": "desc_every"}), + "period": forms.Select(attrs={"aria-describedby": "desc_period"}), + } diff --git a/bookwyrm/forms/author.py b/bookwyrm/forms/author.py new file mode 100644 index 00000000..ca59426d --- /dev/null +++ b/bookwyrm/forms/author.py @@ -0,0 +1,47 @@ +""" using django model forms """ +from django import forms + +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class AuthorForm(CustomForm): + class Meta: + model = models.Author + fields = [ + "last_edited_by", + "name", + "aliases", + "bio", + "wikipedia_link", + "born", + "died", + "openlibrary_key", + "inventaire_id", + "librarything_key", + "goodreads_key", + "isni", + ] + widgets = { + "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), + "aliases": forms.TextInput(attrs={"aria-describedby": "desc_aliases"}), + "bio": forms.Textarea(attrs={"aria-describedby": "desc_bio"}), + "wikipedia_link": forms.TextInput( + attrs={"aria-describedby": "desc_wikipedia_link"} + ), + "born": forms.SelectDateWidget(attrs={"aria-describedby": "desc_born"}), + "died": forms.SelectDateWidget(attrs={"aria-describedby": "desc_died"}), + "oepnlibrary_key": forms.TextInput( + attrs={"aria-describedby": "desc_oepnlibrary_key"} + ), + "inventaire_id": forms.TextInput( + attrs={"aria-describedby": "desc_inventaire_id"} + ), + "librarything_key": forms.TextInput( + attrs={"aria-describedby": "desc_librarything_key"} + ), + "goodreads_key": forms.TextInput( + attrs={"aria-describedby": "desc_goodreads_key"} + ), + } diff --git a/bookwyrm/forms/books.py b/bookwyrm/forms/books.py new file mode 100644 index 00000000..9b3c8401 --- /dev/null +++ b/bookwyrm/forms/books.py @@ -0,0 +1,104 @@ +""" using django model forms """ +from django import forms + +from bookwyrm import models +from bookwyrm.models.fields import ClearableFileInputWithWarning +from .custom_form import CustomForm +from .widgets import ArrayWidget, SelectDateWidget, Select + + +# pylint: disable=missing-class-docstring +class CoverForm(CustomForm): + class Meta: + model = models.Book + fields = ["cover"] + help_texts = {f: None for f in fields} + + +class EditionForm(CustomForm): + class Meta: + model = models.Edition + exclude = [ + "remote_id", + "origin_id", + "created_date", + "updated_date", + "edition_rank", + "authors", + "parent_work", + "shelves", + "connector", + "search_vector", + "links", + "file_links", + ] + widgets = { + "title": forms.TextInput(attrs={"aria-describedby": "desc_title"}), + "subtitle": forms.TextInput(attrs={"aria-describedby": "desc_subtitle"}), + "description": forms.Textarea( + attrs={"aria-describedby": "desc_description"} + ), + "series": forms.TextInput(attrs={"aria-describedby": "desc_series"}), + "series_number": forms.TextInput( + attrs={"aria-describedby": "desc_series_number"} + ), + "subjects": ArrayWidget(), + "languages": forms.TextInput( + attrs={"aria-describedby": "desc_languages_help desc_languages"} + ), + "publishers": forms.TextInput( + attrs={"aria-describedby": "desc_publishers_help desc_publishers"} + ), + "first_published_date": SelectDateWidget( + attrs={"aria-describedby": "desc_first_published_date"} + ), + "published_date": SelectDateWidget( + attrs={"aria-describedby": "desc_published_date"} + ), + "cover": ClearableFileInputWithWarning( + attrs={"aria-describedby": "desc_cover"} + ), + "physical_format": Select( + attrs={"aria-describedby": "desc_physical_format"} + ), + "physical_format_detail": forms.TextInput( + attrs={"aria-describedby": "desc_physical_format_detail"} + ), + "pages": forms.NumberInput(attrs={"aria-describedby": "desc_pages"}), + "isbn_13": forms.TextInput(attrs={"aria-describedby": "desc_isbn_13"}), + "isbn_10": forms.TextInput(attrs={"aria-describedby": "desc_isbn_10"}), + "openlibrary_key": forms.TextInput( + attrs={"aria-describedby": "desc_openlibrary_key"} + ), + "inventaire_id": forms.TextInput( + attrs={"aria-describedby": "desc_inventaire_id"} + ), + "oclc_number": forms.TextInput( + attrs={"aria-describedby": "desc_oclc_number"} + ), + "ASIN": forms.TextInput(attrs={"aria-describedby": "desc_ASIN"}), + } + + +class EditionFromWorkForm(CustomForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # make all fields hidden + for visible in self.visible_fields(): + visible.field.widget = forms.HiddenInput() + + class Meta: + model = models.Work + fields = [ + "title", + "subtitle", + "authors", + "description", + "languages", + "series", + "series_number", + "subjects", + "subject_places", + "cover", + "first_published_date", + ] diff --git a/bookwyrm/forms/custom_form.py b/bookwyrm/forms/custom_form.py new file mode 100644 index 00000000..74a3417a --- /dev/null +++ b/bookwyrm/forms/custom_form.py @@ -0,0 +1,26 @@ +""" Overrides django's default form class """ +from collections import defaultdict +from django.forms import ModelForm +from django.forms.widgets import Textarea + + +class CustomForm(ModelForm): + """add css classes to the forms""" + + def __init__(self, *args, **kwargs): + css_classes = defaultdict(lambda: "") + css_classes["text"] = "input" + css_classes["password"] = "input" + css_classes["email"] = "input" + css_classes["number"] = "input" + css_classes["checkbox"] = "checkbox" + css_classes["textarea"] = "textarea" + # pylint: disable=super-with-arguments + super(CustomForm, self).__init__(*args, **kwargs) + for visible in self.visible_fields(): + if hasattr(visible.field.widget, "input_type"): + input_type = visible.field.widget.input_type + if isinstance(visible.field.widget, Textarea): + input_type = "textarea" + visible.field.widget.attrs["rows"] = 5 + visible.field.widget.attrs["class"] = css_classes[input_type] diff --git a/bookwyrm/forms/edit_user.py b/bookwyrm/forms/edit_user.py new file mode 100644 index 00000000..d609f15d --- /dev/null +++ b/bookwyrm/forms/edit_user.py @@ -0,0 +1,68 @@ +""" using django model forms """ +from django import forms + +from bookwyrm import models +from bookwyrm.models.fields import ClearableFileInputWithWarning +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class EditUserForm(CustomForm): + class Meta: + model = models.User + fields = [ + "avatar", + "name", + "email", + "summary", + "show_goal", + "show_suggested_users", + "manually_approves_followers", + "default_post_privacy", + "discoverable", + "hide_follows", + "preferred_timezone", + "preferred_language", + "theme", + ] + help_texts = {f: None for f in fields} + widgets = { + "avatar": ClearableFileInputWithWarning( + attrs={"aria-describedby": "desc_avatar"} + ), + "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), + "summary": forms.Textarea(attrs={"aria-describedby": "desc_summary"}), + "email": forms.EmailInput(attrs={"aria-describedby": "desc_email"}), + "discoverable": forms.CheckboxInput( + attrs={"aria-describedby": "desc_discoverable"} + ), + } + + +class LimitedEditUserForm(CustomForm): + class Meta: + model = models.User + fields = [ + "avatar", + "name", + "summary", + "manually_approves_followers", + "discoverable", + ] + help_texts = {f: None for f in fields} + widgets = { + "avatar": ClearableFileInputWithWarning( + attrs={"aria-describedby": "desc_avatar"} + ), + "name": forms.TextInput(attrs={"aria-describedby": "desc_name"}), + "summary": forms.Textarea(attrs={"aria-describedby": "desc_summary"}), + "discoverable": forms.CheckboxInput( + attrs={"aria-describedby": "desc_discoverable"} + ), + } + + +class DeleteUserForm(CustomForm): + class Meta: + model = models.User + fields = ["password"] diff --git a/bookwyrm/forms/forms.py b/bookwyrm/forms/forms.py new file mode 100644 index 00000000..9d8f9f39 --- /dev/null +++ b/bookwyrm/forms/forms.py @@ -0,0 +1,59 @@ +""" using django model forms """ +from django import forms +from django.forms import widgets +from django.utils.translation import gettext_lazy as _ + +from bookwyrm import models +from bookwyrm.models.user import FeedFilterChoices +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class FeedStatusTypesForm(CustomForm): + class Meta: + model = models.User + fields = ["feed_status_types"] + help_texts = {f: None for f in fields} + widgets = { + "feed_status_types": widgets.CheckboxSelectMultiple( + choices=FeedFilterChoices, + ), + } + + +class ImportForm(forms.Form): + csv_file = forms.FileField() + + +class ShelfForm(CustomForm): + class Meta: + model = models.Shelf + fields = ["user", "name", "privacy", "description"] + + +class GoalForm(CustomForm): + class Meta: + model = models.AnnualGoal + fields = ["user", "year", "goal", "privacy"] + + +class ReportForm(CustomForm): + class Meta: + model = models.Report + fields = ["user", "reporter", "status", "links", "note"] + + +class ReadThroughForm(CustomForm): + def clean(self): + """don't let readthroughs end before they start""" + cleaned_data = super().clean() + start_date = cleaned_data.get("start_date") + finish_date = cleaned_data.get("finish_date") + if start_date and finish_date and start_date > finish_date: + self.add_error( + "finish_date", _("Reading finish date cannot be before start date.") + ) + + class Meta: + model = models.ReadThrough + fields = ["user", "book", "start_date", "finish_date"] diff --git a/bookwyrm/forms/groups.py b/bookwyrm/forms/groups.py new file mode 100644 index 00000000..15b27c0a --- /dev/null +++ b/bookwyrm/forms/groups.py @@ -0,0 +1,16 @@ +""" using django model forms """ +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class UserGroupForm(CustomForm): + class Meta: + model = models.User + fields = ["groups"] + + +class GroupForm(CustomForm): + class Meta: + model = models.Group + fields = ["user", "privacy", "name", "description"] diff --git a/bookwyrm/forms/landing.py b/bookwyrm/forms/landing.py new file mode 100644 index 00000000..b01c2cc9 --- /dev/null +++ b/bookwyrm/forms/landing.py @@ -0,0 +1,45 @@ +""" Forms for the landing pages """ +from django.forms import PasswordInput +from django.utils.translation import gettext_lazy as _ + +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class LoginForm(CustomForm): + class Meta: + model = models.User + fields = ["localname", "password"] + help_texts = {f: None for f in fields} + widgets = { + "password": PasswordInput(), + } + + +class RegisterForm(CustomForm): + class Meta: + model = models.User + fields = ["localname", "email", "password"] + help_texts = {f: None for f in fields} + widgets = {"password": PasswordInput()} + + def clean(self): + """Check if the username is taken""" + cleaned_data = super().clean() + localname = cleaned_data.get("localname").strip() + if models.User.objects.filter(localname=localname).first(): + self.add_error("localname", _("User with this username already exists")) + + +class InviteRequestForm(CustomForm): + def clean(self): + """make sure the email isn't in use by a registered user""" + cleaned_data = super().clean() + email = cleaned_data.get("email") + if email and models.User.objects.filter(email=email).exists(): + self.add_error("email", _("A user with this email already exists.")) + + class Meta: + model = models.InviteRequest + fields = ["email", "answer"] diff --git a/bookwyrm/forms/links.py b/bookwyrm/forms/links.py new file mode 100644 index 00000000..de229bc2 --- /dev/null +++ b/bookwyrm/forms/links.py @@ -0,0 +1,48 @@ +""" using django model forms """ +from urllib.parse import urlparse + +from django.utils.translation import gettext_lazy as _ + +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class LinkDomainForm(CustomForm): + class Meta: + model = models.LinkDomain + fields = ["name"] + + +class FileLinkForm(CustomForm): + class Meta: + model = models.FileLink + fields = ["url", "filetype", "availability", "book", "added_by"] + + def clean(self): + """make sure the domain isn't blocked or pending""" + cleaned_data = super().clean() + url = cleaned_data.get("url") + filetype = cleaned_data.get("filetype") + book = cleaned_data.get("book") + domain = urlparse(url).netloc + if models.LinkDomain.objects.filter(domain=domain).exists(): + status = models.LinkDomain.objects.get(domain=domain).status + if status == "blocked": + # pylint: disable=line-too-long + self.add_error( + "url", + _( + "This domain is blocked. Please contact your administrator if you think this is an error." + ), + ) + elif 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." + ), + ) diff --git a/bookwyrm/forms/lists.py b/bookwyrm/forms/lists.py new file mode 100644 index 00000000..647db3bf --- /dev/null +++ b/bookwyrm/forms/lists.py @@ -0,0 +1,37 @@ +""" using django model forms """ +from django import forms +from django.forms import ChoiceField +from django.utils.translation import gettext_lazy as _ + +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class ListForm(CustomForm): + class Meta: + model = models.List + fields = ["user", "name", "description", "curation", "privacy", "group"] + + +class ListItemForm(CustomForm): + class Meta: + model = models.ListItem + fields = ["user", "book", "book_list", "notes"] + + +class SortListForm(forms.Form): + sort_by = ChoiceField( + choices=( + ("order", _("List Order")), + ("title", _("Book Title")), + ("rating", _("Rating")), + ), + label=_("Sort By"), + ) + direction = ChoiceField( + choices=( + ("ascending", _("Ascending")), + ("descending", _("Descending")), + ), + ) diff --git a/bookwyrm/forms/status.py b/bookwyrm/forms/status.py new file mode 100644 index 00000000..0800166b --- /dev/null +++ b/bookwyrm/forms/status.py @@ -0,0 +1,82 @@ +""" using django model forms """ +from bookwyrm import models +from .custom_form import CustomForm + + +# pylint: disable=missing-class-docstring +class RatingForm(CustomForm): + class Meta: + model = models.ReviewRating + fields = ["user", "book", "rating", "privacy"] + + +class ReviewForm(CustomForm): + class Meta: + model = models.Review + fields = [ + "user", + "book", + "name", + "content", + "rating", + "content_warning", + "sensitive", + "privacy", + ] + + +class CommentForm(CustomForm): + class Meta: + model = models.Comment + fields = [ + "user", + "book", + "content", + "content_warning", + "sensitive", + "privacy", + "progress", + "progress_mode", + "reading_status", + ] + + +class QuotationForm(CustomForm): + class Meta: + model = models.Quotation + fields = [ + "user", + "book", + "quote", + "content", + "content_warning", + "sensitive", + "privacy", + "position", + "position_mode", + ] + + +class ReplyForm(CustomForm): + class Meta: + model = models.Status + fields = [ + "user", + "content", + "content_warning", + "sensitive", + "reply_parent", + "privacy", + ] + + +class StatusForm(CustomForm): + class Meta: + model = models.Status + fields = ["user", "content", "content_warning", "sensitive", "privacy"] + + +class DirectForm(CustomForm): + class Meta: + model = models.Status + fields = ["user", "content", "content_warning", "sensitive", "privacy"] diff --git a/bookwyrm/forms/widgets.py b/bookwyrm/forms/widgets.py new file mode 100644 index 00000000..ee9345aa --- /dev/null +++ b/bookwyrm/forms/widgets.py @@ -0,0 +1,70 @@ +""" using django model forms """ +from django import forms + + +class ArrayWidget(forms.widgets.TextInput): + """Inputs for postgres array fields""" + + # pylint: disable=unused-argument + # pylint: disable=no-self-use + def value_from_datadict(self, data, files, name): + """get all values for this name""" + return [i for i in data.getlist(name) if i] + + +class Select(forms.Select): + """custom template for select widget""" + + template_name = "widgets/select.html" + + +class SelectDateWidget(forms.SelectDateWidget): + """ + A widget that splits date input into two +
- + {% for list in list_options %} {% endfor %} @@ -385,6 +403,6 @@ {% endblock %} {% block scripts %} - + {% endblock %} diff --git a/bookwyrm/templates/book/cover_add_modal.html b/bookwyrm/templates/book/cover_add_modal.html index e8207ff4..8ca5bf2a 100644 --- a/bookwyrm/templates/book/cover_add_modal.html +++ b/bookwyrm/templates/book/cover_add_modal.html @@ -28,8 +28,10 @@ {% endblock %} {% block modal-footer %} - - +
+ + +
{% endblock %} {% block modal-form-close %}{% endblock %} diff --git a/bookwyrm/templates/book/edit/edit_book.html b/bookwyrm/templates/book/edit/edit_book.html index 3d41058e..b088c1e8 100644 --- a/bookwyrm/templates/book/edit/edit_book.html +++ b/bookwyrm/templates/book/edit/edit_book.html @@ -3,18 +3,24 @@ {% load humanize %} {% load utilities %} -{% block title %}{% if book %}{% blocktrans with book_title=book.title %}Edit "{{ book_title }}"{% endblocktrans %}{% else %}{% trans "Add Book" %}{% endif %}{% endblock %} +{% block title %} + {% if book.title %} + {% blocktrans with book_title=book.title %}Edit "{{ book_title }}"{% endblocktrans %} + {% else %} + {% trans "Add Book" %} + {% endif %} +{% endblock %} {% block content %}

- {% if book %} + {% if book.title %} {% blocktrans with book_title=book.title %}Edit "{{ book_title }}"{% endblocktrans %} {% else %} {% trans "Add Book" %} {% endif %}

- {% if book %} + {% if book.created_date %}
{% trans "Added:" %}
{{ book.created_date | naturaltime }}
@@ -33,7 +39,7 @@
{{ match.parent_work.title }} {% endfor %} -
@@ -60,7 +63,7 @@ {% trans "Series number:" %} {{ form.series_number }} - + {% include 'snippets/form_errors.html' with errors_list=form.series_number.errors id="desc_series_number" %}
@@ -74,9 +77,60 @@ {% trans "Separate multiple values with commas." %} - + {% include 'snippets/form_errors.html' with errors_list=form.languages.errors id="desc_languages" %} + +
+ + {% for subject in book.subjects %} + +
+
+ +
+
+ +
+
+ {% endfor %} + + + {% include 'snippets/form_errors.html' with errors_list=form.subjects.errors id="desc_subjects" %} +
+ + + + @@ -93,7 +147,7 @@ {% trans "Separate multiple values with commas." %} - + {% include 'snippets/form_errors.html' with errors_list=form.publishers.errors id="desc_publishers" %} @@ -101,8 +155,7 @@ - - + {{ form.first_published_date }} {% include 'snippets/form_errors.html' with errors_list=form.first_published_date.errors id="desc_first_published_date" %} @@ -110,8 +163,8 @@ - - + {{ form.published_date }} + {% include 'snippets/form_errors.html' with errors_list=form.published_date.errors id="desc_published_date" %} @@ -123,6 +176,8 @@
{% if book.authors.exists %} + {# preserve authors if the book is unsaved #} +
{% for author in book.authors.all %}
@@ -149,7 +204,12 @@ {% endfor %}
- + + +
@@ -180,7 +240,7 @@ - + {% include 'snippets/form_errors.html' with errors_list=form.cover.errors id="desc_cover" %} @@ -198,10 +258,8 @@ -
- {{ form.physical_format }} -
- + {{ form.physical_format }} + {% include 'snippets/form_errors.html' with errors_list=form.physical_format.errors id="desc_physical_format" %} @@ -211,7 +269,7 @@ {% trans "Format details:" %} {{ form.physical_format_detail }} - + {% include 'snippets/form_errors.html' with errors_list=form.physical_format_detail.errors id="desc_physical_format_detail" %} @@ -222,7 +280,7 @@ {% trans "Pages:" %} {{ form.pages }} - + {% include 'snippets/form_errors.html' with errors_list=form.pages.errors id="desc_pages" %} @@ -238,7 +296,7 @@ {% trans "ISBN 13:" %} {{ form.isbn_13 }} - + {% include 'snippets/form_errors.html' with errors_list=form.isbn_13.errors id="desc_isbn_13" %} @@ -247,7 +305,7 @@ {% trans "ISBN 10:" %} {{ form.isbn_10 }} - + {% include 'snippets/form_errors.html' with errors_list=form.isbn_10.errors id="desc_isbn_10" %} @@ -256,7 +314,7 @@ {% trans "Openlibrary ID:" %} {{ form.openlibrary_key }} - + {% include 'snippets/form_errors.html' with errors_list=form.openlibrary_key.errors id="desc_openlibrary_key" %} @@ -265,7 +323,7 @@ {% trans "Inventaire ID:" %} {{ form.inventaire_id }} - + {% include 'snippets/form_errors.html' with errors_list=form.inventaire_id.errors id="desc_inventaire_id" %} @@ -274,7 +332,7 @@ {% trans "OCLC Number:" %} {{ form.oclc_number }} - + {% include 'snippets/form_errors.html' with errors_list=form.oclc_number.errors id="desc_oclc_number" %} @@ -283,10 +341,14 @@ {% trans "ASIN:" %} {{ form.asin }} - + {% include 'snippets/form_errors.html' with errors_list=form.ASIN.errors id="desc_ASIN" %} + +{% block scripts %} + +{% endblock %} diff --git a/bookwyrm/templates/book/editions/editions.html b/bookwyrm/templates/book/editions/editions.html index a3ff0802..e15e7a74 100644 --- a/bookwyrm/templates/book/editions/editions.html +++ b/bookwyrm/templates/book/editions/editions.html @@ -46,7 +46,36 @@ {% endfor %} -
+
{% include 'snippets/pagination.html' with page=editions path=request.path %}
+ +
+

+ {% trans "Can't find the edition you're looking for?" %} +

+ + + {% csrf_token %} + {{ work_form.title }} + {{ work_form.subtitle }} + {{ work_form.authors }} + {{ work_form.description }} + {{ work_form.languages }} + {{ work_form.series }} + {{ work_form.cover }} + {{ work_form.first_published_date }} + {% for subject in work.subjects %} + + {% endfor %} + + +
+ +
+ +
+ {% endblock %} diff --git a/bookwyrm/templates/book/editions/format_filter.html b/bookwyrm/templates/book/editions/format_filter.html index c722b24f..4e0f3c9b 100644 --- a/bookwyrm/templates/book/editions/format_filter.html +++ b/bookwyrm/templates/book/editions/format_filter.html @@ -2,15 +2,17 @@ {% load i18n %} {% block filter %} - -
- +
+ +
+ +
{% endblock %} diff --git a/bookwyrm/templates/book/editions/language_filter.html b/bookwyrm/templates/book/editions/language_filter.html index d9051fd8..d14e3353 100644 --- a/bookwyrm/templates/book/editions/language_filter.html +++ b/bookwyrm/templates/book/editions/language_filter.html @@ -2,15 +2,17 @@ {% load i18n %} {% block filter %} - -
- +
+ +
+ +
{% endblock %} diff --git a/bookwyrm/templates/book/editions/search_filter.html b/bookwyrm/templates/book/editions/search_filter.html index f2345a68..91c76422 100644 --- a/bookwyrm/templates/book/editions/search_filter.html +++ b/bookwyrm/templates/book/editions/search_filter.html @@ -2,7 +2,9 @@ {% load i18n %} {% block filter %} - - +
+ + +
{% endblock %} diff --git a/bookwyrm/templates/book/file_links/add_link_modal.html b/bookwyrm/templates/book/file_links/add_link_modal.html index 0002b82b..67b437bd 100644 --- a/bookwyrm/templates/book/file_links/add_link_modal.html +++ b/bookwyrm/templates/book/file_links/add_link_modal.html @@ -55,10 +55,10 @@ {% endblock %} {% block modal-footer %} - -{% if not static %} +
-{% endif %} - + +
{% endblock %} + {% block modal-form-close %}{% endblock %} diff --git a/bookwyrm/templates/book/file_links/edit_links.html b/bookwyrm/templates/book/file_links/edit_links.html index 39d3b998..a088a40d 100644 --- a/bookwyrm/templates/book/file_links/edit_links.html +++ b/bookwyrm/templates/book/file_links/edit_links.html @@ -6,24 +6,24 @@ {% block content %} -
+

- {% blocktrans with title=book|book_title %} + {% blocktrans trimmed with title=book|book_title %} Links for "{{ title }}" {% endblocktrans %}

-
-
+ +
diff --git a/bookwyrm/templates/book/file_links/verification_modal.html b/bookwyrm/templates/book/file_links/verification_modal.html index 81685da0..01f17f96 100644 --- a/bookwyrm/templates/book/file_links/verification_modal.html +++ b/bookwyrm/templates/book/file_links/verification_modal.html @@ -17,13 +17,13 @@ Is that where you'd like to go? {% block modal-footer %} -{% trans "Continue" %} - - {% if request.user.is_authenticated %} -
+ + + +{% trans "Continue" %} {% endif %} {% endblock %} diff --git a/bookwyrm/templates/book/sync_modal.html b/bookwyrm/templates/book/sync_modal.html index 6e5df0c0..81ad8db9 100644 --- a/bookwyrm/templates/book/sync_modal.html +++ b/bookwyrm/templates/book/sync_modal.html @@ -19,8 +19,10 @@ {% endblock %} {% block modal-footer %} - - +
+ + +
{% endblock %} {% block modal-form-close %}{% endblock %} diff --git a/bookwyrm/templates/components/card.html b/bookwyrm/templates/components/card.html index f1104893..7e38bd40 100644 --- a/bookwyrm/templates/components/card.html +++ b/bookwyrm/templates/components/card.html @@ -11,7 +11,7 @@
{% endif %} -
+
{% block card-footer %} {% endblock %}
diff --git a/bookwyrm/templates/components/inline_form.html b/bookwyrm/templates/components/inline_form.html index 37f9f556..8cb1834f 100644 --- a/bookwyrm/templates/components/inline_form.html +++ b/bookwyrm/templates/components/inline_form.html @@ -1,6 +1,6 @@ {% load i18n %}
-
+

{% block header %}{% endblock %}

diff --git a/bookwyrm/templates/components/tooltip.html b/bookwyrm/templates/components/tooltip.html deleted file mode 100644 index b1a8f56c..00000000 --- a/bookwyrm/templates/components/tooltip.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n %} - -{% trans "Help" as button_text %} -{% include 'snippets/toggle/open_button.html' with text=button_text class="ml-3 is-rounded is-small is-white p-0 pb-1" icon="question-circle is-size-6" controls_text=controls_text controls_uid=controls_uid %} - - diff --git a/bookwyrm/templates/confirm_email/confirm_email.html b/bookwyrm/templates/confirm_email/confirm_email.html index 8c8adcdd..abdd3a73 100644 --- a/bookwyrm/templates/confirm_email/confirm_email.html +++ b/bookwyrm/templates/confirm_email/confirm_email.html @@ -29,9 +29,16 @@
- {% trans "Can't find your code?" as button_text %} - {% include "snippets/toggle/open_button.html" with text=button_text controls_text="resend_form" focus="resend_form_header" %} - {% include "confirm_email/resend_form.html" with controls_text="resend_form" %} +
+ +
+ {% include "confirm_email/resend_modal.html" with id="resend_form" %}
diff --git a/bookwyrm/templates/confirm_email/resend.html b/bookwyrm/templates/confirm_email/resend.html new file mode 100644 index 00000000..221f0756 --- /dev/null +++ b/bookwyrm/templates/confirm_email/resend.html @@ -0,0 +1,10 @@ +{% extends 'landing/layout.html' %} +{% load i18n %} + +{% block title %} +{% trans "Resend confirmation link" %} +{% endblock %} + +{% block content %} +{% include "confirm_email/resend_modal.html" with active=True static=True id="resend-modal" %} +{% endblock %} diff --git a/bookwyrm/templates/confirm_email/resend_form.html b/bookwyrm/templates/confirm_email/resend_form.html deleted file mode 100644 index 7c0c1098..00000000 --- a/bookwyrm/templates/confirm_email/resend_form.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "components/inline_form.html" %} -{% load i18n %} -{% block header %} -{% trans "Resend confirmation link" %} -{% endblock %} - -{% block form %} -
- {% csrf_token %} -
- -
- -
-
-
- -
-
-{% endblock %} diff --git a/bookwyrm/templates/confirm_email/resend_modal.html b/bookwyrm/templates/confirm_email/resend_modal.html new file mode 100644 index 00000000..beb9318a --- /dev/null +++ b/bookwyrm/templates/confirm_email/resend_modal.html @@ -0,0 +1,44 @@ +{% extends "components/modal.html" %} +{% load i18n %} + +{% block modal-title %} +{% trans "Resend confirmation link" %} +{% endblock %} + +{% block modal-form-open %} +
+{% endblock %} + +{% block modal-body %} +{% csrf_token %} +
+ +
+ + {% if error %} +
+

+ {% trans "No user matching this email address found." %} +

+
+ {% endif %} +
+
+{% endblock %} + +{% block modal-footer %} +
+ +
+{% endblock %} + +{% block modal-form-close %} +
+{% endblock %} diff --git a/bookwyrm/templates/directory/user_card.html b/bookwyrm/templates/directory/user_card.html index ccae925a..5a17dbe4 100644 --- a/bookwyrm/templates/directory/user_card.html +++ b/bookwyrm/templates/directory/user_card.html @@ -33,7 +33,7 @@
+{% endblock %} + {% block scripts %}{% endblock %} diff --git a/bookwyrm/templates/lists/add_item_modal.html b/bookwyrm/templates/lists/add_item_modal.html index 5c210d46..2c586b30 100644 --- a/bookwyrm/templates/lists/add_item_modal.html +++ b/bookwyrm/templates/lists/add_item_modal.html @@ -32,14 +32,16 @@ {% endblock %} {% block modal-footer %} - - +
+ + +
{% endblock %} {% block modal-form-close %}{% endblock %} diff --git a/bookwyrm/templates/lists/delete_list_modal.html b/bookwyrm/templates/lists/delete_list_modal.html index aad87952..88e04423 100644 --- a/bookwyrm/templates/lists/delete_list_modal.html +++ b/bookwyrm/templates/lists/delete_list_modal.html @@ -8,15 +8,17 @@ {% endblock %} {% block modal-footer %} -
+ {% csrf_token %} - - +
+ + +
{% endblock %} diff --git a/bookwyrm/templates/lists/form.html b/bookwyrm/templates/lists/form.html index c8383d9c..3558d8cc 100644 --- a/bookwyrm/templates/lists/form.html +++ b/bookwyrm/templates/lists/form.html @@ -114,7 +114,7 @@ -
+
{% if list.id %}
- + +
+ + +
{% endblock %} diff --git a/bookwyrm/templates/readthrough/readthrough_list.html b/bookwyrm/templates/readthrough/readthrough_list.html index 9f9db9a5..d0d679da 100644 --- a/bookwyrm/templates/readthrough/readthrough_list.html +++ b/bookwyrm/templates/readthrough/readthrough_list.html @@ -3,7 +3,7 @@ {% load tz %} {% load utilities %}
-
+
{% trans "Progress Updates:" %} diff --git a/bookwyrm/templates/readthrough/readthrough_modal.html b/bookwyrm/templates/readthrough/readthrough_modal.html index 07d27b66..e7fffb19 100644 --- a/bookwyrm/templates/readthrough/readthrough_modal.html +++ b/bookwyrm/templates/readthrough/readthrough_modal.html @@ -17,7 +17,14 @@ {% endblock %} {% block modal-form-open %} -
+ {% endblock %} {% block modal-body %} @@ -69,10 +76,10 @@ {% endblock %} {% block modal-footer %} - -{% if not static %} - -{% endif %} +
+ + +
{% endblock %} {% block modal-form-close %} diff --git a/bookwyrm/templates/report.html b/bookwyrm/templates/report.html index 686401eb..be7ed68f 100644 --- a/bookwyrm/templates/report.html +++ b/bookwyrm/templates/report.html @@ -6,5 +6,5 @@ {% endblock %} {% block content %} -{% include "snippets/report_modal.html" with user=user active=True static=True %} +{% include "snippets/report_modal.html" with user=user active=True static=True id="report-modal" %} {% endblock %} diff --git a/bookwyrm/templates/search/barcode_modal.html b/bookwyrm/templates/search/barcode_modal.html new file mode 100644 index 00000000..07e95f59 --- /dev/null +++ b/bookwyrm/templates/search/barcode_modal.html @@ -0,0 +1,48 @@ +{% extends 'components/modal.html' %} +{% load i18n %} + +{% block modal-title %} + {% blocktrans %} + Scan Barcode + {% endblocktrans %} +{% endblock %} + +{% block modal-body %} +
+
+
+ +
+ +
+ +
+ + + + +
+{% endblock %} + +{% block modal-footer %} + +{% endblock %} + + diff --git a/bookwyrm/templates/search/book.html b/bookwyrm/templates/search/book.html index cc615d50..7096a55d 100644 --- a/bookwyrm/templates/search/book.html +++ b/bookwyrm/templates/search/book.html @@ -45,7 +45,7 @@ {{ result_set.connector.name|default:result_set.connector.identifier }} - + {% endif %} diff --git a/bookwyrm/templates/settings/announcements/announcement.html b/bookwyrm/templates/settings/announcements/announcement.html index 8b49f4f4..03f012b4 100644 --- a/bookwyrm/templates/settings/announcements/announcement.html +++ b/bookwyrm/templates/settings/announcements/announcement.html @@ -1,17 +1,20 @@ {% extends 'settings/layout.html' %} -{% load i18n %}{% load humanize %} +{% load i18n %} +{% load humanize %} + {% block title %}{% trans "Announcement" %} - {{ announcement.preview }}{% endblock %} {% block header %} {% trans "Announcement" %} -{% trans "Back to list" %} {% endblock %} {% block edit-button %} -{% trans "Edit Announcement" as button_text %}
- {% include 'snippets/toggle/open_button.html' with controls_text="edit_announcement" icon_with_text="pencil" text=button_text focus="edit_announcement_header" %} + + + {% trans "Edit" %} +
{% csrf_token %} @@ -23,12 +26,20 @@
{% endblock %} +{% block breadcrumbs %} + +{% endblock %} + {% block panel %} - - - {% include 'settings/announcements/announcement_form.html' with controls_text="edit_announcement" %} -
-
{% trans "Visible:" %}
diff --git a/bookwyrm/templates/settings/announcements/announcement_form.html b/bookwyrm/templates/settings/announcements/announcement_form.html deleted file mode 100644 index 8f68e255..00000000 --- a/bookwyrm/templates/settings/announcements/announcement_form.html +++ /dev/null @@ -1,80 +0,0 @@ -{% extends 'components/inline_form.html' %} -{% load i18n %} - -{% block header %} -{% if announcement %} -{% trans "Edit Announcement" %} -{% else %} -{% trans "Create Announcement" %} -{% endif %} -{% endblock %} - -{% block form %} -{% csrf_token %} - -

- - {{ form.preview }} - - {% include 'snippets/form_errors.html' with errors_list=form.preview.errors id="desc_preview" %} -

-

- - {{ form.content }} - - {% include 'snippets/form_errors.html' with errors_list=form.content.errors id="desc_content" %} -

-

- - - - {% include 'snippets/form_errors.html' with errors_list=form.event_date.errors id="desc_event_date" %} -

- -
-
-

- - - - {% include 'snippets/form_errors.html' with errors_list=form.start_date.errors id="desc_start_date" %} -

-
-
-

- - - - {% include 'snippets/form_errors.html' with errors_list=form.end_date.errors id="desc_end_date" %} -

-
-
-

- - {{ form.active }} - - {% include 'snippets/form_errors.html' with errors_list=form.active.errors id="desc_active" %} -

-
-
- -
-
- -
-
-{% endblock %} diff --git a/bookwyrm/templates/settings/announcements/announcements.html b/bookwyrm/templates/settings/announcements/announcements.html index c86ecf3e..784fef16 100644 --- a/bookwyrm/templates/settings/announcements/announcements.html +++ b/bookwyrm/templates/settings/announcements/announcements.html @@ -5,17 +5,16 @@ {% block header %}{% trans "Announcements" %}{% endblock %} {% block edit-button %} -{% trans "Create Announcement" as button_text %} -{% include 'snippets/toggle/open_button.html' with controls_text="create_announcement" icon_with_text="plus" text=button_text focus="create_announcement_header" %} + + {% trans "Create Announcement" as text %} + + {{ text }} + {% endblock %} {% block panel %} -
- {% include 'settings/announcements/announcement_form.html' with controls_text="create_announcement" %} -
- -
- +
+
+ {% for announcement in announcements %} @@ -46,6 +48,15 @@ + {% endfor %} {% if not announcements %} diff --git a/bookwyrm/templates/settings/announcements/edit_announcement.html b/bookwyrm/templates/settings/announcements/edit_announcement.html new file mode 100644 index 00000000..372d6973 --- /dev/null +++ b/bookwyrm/templates/settings/announcements/edit_announcement.html @@ -0,0 +1,125 @@ +{% extends 'settings/layout.html' %} +{% load i18n %} + +{% block header %} + {% if announcement %} + {% trans "Edit Announcement" %} + {% else %} + {% trans "Create Announcement" %} + {% endif %} +{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block panel %} + + {% csrf_token %} + +

{% trans "Announcement content" %}

+
+

+ + {{ form.preview }} + + {% include 'snippets/form_errors.html' with errors_list=form.preview.errors id="desc_preview" %} +

+

+ + {{ form.content }} + + {% include 'snippets/form_errors.html' with errors_list=form.content.errors id="desc_content" %} +

+

+ + + + {% include 'snippets/form_errors.html' with errors_list=form.event_date.errors id="desc_event_date" %} +

+
+ +

{% trans "Display settings" %}

+
+
+
+

+ + + + {% include 'snippets/form_errors.html' with errors_list=form.start_date.errors id="desc_start_date" %} +

+
+
+

+ + + + {% include 'snippets/form_errors.html' with errors_list=form.end_date.errors id="desc_end_date" %} +

+
+
+ +
+ {{ form.display_type }} +
+ + {% include 'snippets/form_errors.html' with errors_list=form.active.errors id="desc_display_type" %} +
+
+

+ + + {% include 'snippets/form_errors.html' with errors_list=form.active.errors id="desc_active" %} +

+
+ +
+
+ +
+
+ +{% endblock %} diff --git a/bookwyrm/templates/settings/automod/rules.html b/bookwyrm/templates/settings/automod/rules.html new file mode 100644 index 00000000..128ff20b --- /dev/null +++ b/bookwyrm/templates/settings/automod/rules.html @@ -0,0 +1,200 @@ +{% extends 'settings/layout.html' %} +{% load i18n %} +{% load humanize %} +{% load utilities %} + +{% block title %} +{% trans "Auto-moderation rules" %} +{% endblock %} + +{% block header %} +{% trans "Auto-moderation rules" %} +{% endblock %} + +{% block panel %} + +
+

+ {% trans "Auto-moderation rules will create reports for any local user or status with fields matching the provided string." %} + {% trans "Users or statuses that have already been reported (regardless of whether the report was resolved) will not be flagged." %} +

+
+
+ {% if task %} +
+
+ {% trans "Schedule:" %} +
+
+ {{ task.schedule }} +
+ +
+ {% trans "Last run:" %} +
+
+ {{ task.last_run_at|naturaltime }} +
+ +
+ {% trans "Total run count:" %} +
+
+ {{ task.total_run_count }} +
+ +
+ {% trans "Enabled:" %} +
+
+ + {{ task.enabled|yesno }} + +
+
+ +
+
+ {% csrf_token %} + + +
+ {% csrf_token %} + +

{% trans "Last run date will not be updated" %}

+ +
+ + {% else %} +

{% trans "Schedule scan" %}

+
+ {% csrf_token %} +
+ + {{ task_form.every }} +

+ {{ task_form.every.help_text }} +

+
+
+ +
+ {{ task_form.period }} +
+

+ {{ task_form.period.help_text }} +

+
+ + + {% endif %} +
+ +{% if success %} +
+ + + {% trans "Successfully added rule" %} + +
+{% endif %} + +
+

{% trans "Add Rule" %}

+
+
+ {% csrf_token %} + + +
+
+
+ + {{ form.string_match }} + {% include 'snippets/form_errors.html' with errors_list=form.string_match.errors id="desc_string_match" %} +
+
+ +
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+ +
+

{% trans "Current Rules" %}

+
+ + + {% trans "Show rules" %} ({{ rules.count }}) + + + + +
+
{% url 'settings-announcements' as url %} @@ -38,6 +37,9 @@ {% trans "Status" as text %} {% include 'snippets/table-sort-header.html' with field="active" sort=sort text=text %} + {% trans "Actions" %} +
{{ announcement.start_date|naturaltime|default:'' }} {{ announcement.end_date|naturaltime|default:'' }} {% if announcement.active %}{% trans "active" %}{% else %}{% trans "inactive" %}{% endif %} +
+ {% csrf_token %} + +
+
+ + + + + + + {% for rule in rules %} + + + + + + + {% endfor %} +
+ + + + + + +
+ {{ rule.string_match }} + + {{ rule.flag_users|yesno }} + + {{ rule.flag_statuses|yesno }} + +
+ {% csrf_token %} +
+ +
+
+
+
+ +
+{% endblock %} + diff --git a/bookwyrm/templates/settings/dashboard/dashboard.html b/bookwyrm/templates/settings/dashboard/dashboard.html index 65c666a6..cec3e89a 100644 --- a/bookwyrm/templates/settings/dashboard/dashboard.html +++ b/bookwyrm/templates/settings/dashboard/dashboard.html @@ -10,26 +10,26 @@ {% block panel %}
-
-
+
+

{% trans "Total users" %}

{{ users|intcomma }}

-
-
+
+

{% trans "Active this month" %}

{{ active_users|intcomma }}

-
-
+
+

{% trans "Statuses" %}

{{ statuses|intcomma }}

-
-
+
+

{% trans "Works" %}

{{ works|intcomma }}

@@ -38,8 +38,8 @@
{% if reports %} -
- + {% endif %} + {% if pending_domains %} -
- + {% endif %} + {% if not site.allow_registration and site.allow_invite_requests and invite_requests %} -

{% trans "Instance Activity" %}

-
+
+ +{% endblock %} + +{% block breadcrumbs %} + {% endblock %} {% block panel %} diff --git a/bookwyrm/templates/settings/federation/instance_blocklist.html b/bookwyrm/templates/settings/federation/instance_blocklist.html index abd58091..4f1fe9cb 100644 --- a/bookwyrm/templates/settings/federation/instance_blocklist.html +++ b/bookwyrm/templates/settings/federation/instance_blocklist.html @@ -4,7 +4,19 @@ {% block header %} {% trans "Import Blocklist" %} -{% trans "Back to instance list" %} +{% endblock %} + +{% block breadcrumbs %} + {% endblock %} {% block panel %} diff --git a/bookwyrm/templates/settings/federation/instance_list.html b/bookwyrm/templates/settings/federation/instance_list.html index 61afb317..89c50e5e 100644 --- a/bookwyrm/templates/settings/federation/instance_list.html +++ b/bookwyrm/templates/settings/federation/instance_list.html @@ -16,16 +16,16 @@
- +
{% url 'settings-federation' as url %} + {% if site.invite_request_question %} + + {% endif %} + {% if site.invite_request_question %} + + {% endif %}
diff --git a/bookwyrm/templates/settings/invites/manage_invite_requests.html b/bookwyrm/templates/settings/invites/manage_invite_requests.html index fb7c0b1f..bdd60099 100644 --- a/bookwyrm/templates/settings/invites/manage_invite_requests.html +++ b/bookwyrm/templates/settings/invites/manage_invite_requests.html @@ -40,6 +40,9 @@ {% include 'snippets/table-sort-header.html' with field="invite__invitees__created_date" sort=sort text=text %} {% trans "Email" %}{% trans "Answer" %} {% trans "Status" as text %} {% include 'snippets/table-sort-header.html' with field="invite__times_used" sort=sort text=text %} @@ -54,6 +57,9 @@ {{ req.created_date | naturaltime }} {{ req.invite.invitees.first.created_date | naturaltime }} {{ req.email }}{{ req.answer }} {% if req.invite.times_used %} {% trans "Accepted" %} diff --git a/bookwyrm/templates/settings/ip_blocklist/ip_address_form.html b/bookwyrm/templates/settings/ip_blocklist/ip_address_form.html index 4a776987..2afc00b4 100644 --- a/bookwyrm/templates/settings/ip_blocklist/ip_address_form.html +++ b/bookwyrm/templates/settings/ip_blocklist/ip_address_form.html @@ -21,6 +21,7 @@
+

{% trans "You can block IP ranges using CIDR syntax." %}

{% include 'snippets/form_errors.html' with errors_list=form.address.errors id="desc_address" %} diff --git a/bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html b/bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html deleted file mode 100644 index 3a2bf543..00000000 --- a/bookwyrm/templates/settings/ip_blocklist/ip_tooltip.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends 'components/tooltip.html' %} -{% load i18n %} - -{% block tooltip_content %} - -{% trans "You can block IP ranges using CIDR syntax." %} - -{% endblock %} diff --git a/bookwyrm/templates/settings/layout.html b/bookwyrm/templates/settings/layout.html index 1addc8db..862de958 100644 --- a/bookwyrm/templates/settings/layout.html +++ b/bookwyrm/templates/settings/layout.html @@ -9,6 +9,7 @@

{% block header %}{% endblock %}

+ {% block breadcrumbs %}{% endblock %}
{% block edit-button %}{% endblock %} @@ -16,6 +17,7 @@
+
-
+
{% block panel %}{% endblock %}
diff --git a/bookwyrm/templates/settings/link_domains/edit_domain_modal.html b/bookwyrm/templates/settings/link_domains/edit_domain_modal.html index 135783e7..06ab360c 100644 --- a/bookwyrm/templates/settings/link_domains/edit_domain_modal.html +++ b/bookwyrm/templates/settings/link_domains/edit_domain_modal.html @@ -18,8 +18,10 @@ {% endblock %} {% block modal-footer %} - - +
+ + +
{% endblock %} {% block modal-form-close %}{% endblock %} diff --git a/bookwyrm/templates/settings/link_domains/link_domains.html b/bookwyrm/templates/settings/link_domains/link_domains.html index ceb0d8cb..b633c5fb 100644 --- a/bookwyrm/templates/settings/link_domains/link_domains.html +++ b/bookwyrm/templates/settings/link_domains/link_domains.html @@ -53,7 +53,7 @@ {% trans "View links" %} ({{ domain.links.count }}) - +
diff --git a/bookwyrm/templates/settings/reports/report.html b/bookwyrm/templates/settings/reports/report.html index 84fafb14..20fdeca3 100644 --- a/bookwyrm/templates/settings/reports/report.html +++ b/bookwyrm/templates/settings/reports/report.html @@ -1,6 +1,7 @@ {% extends 'settings/layout.html' %} {% load i18n %} {% load humanize %} +{% load feed_page_tags %} {% block title %} {% include "settings/reports/report_header.html" with report=report %} @@ -17,20 +18,27 @@ {% include 'settings/reports/report_preview.html' with report=report %}
-{% if report.statuses.exists %}
-

{% trans "Reported statuses" %}

-
    - {% for status in report.statuses.select_subclasses.all %} -
  • - {% if status.deleted %} - {% trans "Status has been deleted" %} - {% else %} - {% include 'snippets/status/status.html' with status=status moderation_mode=True %} - {% endif %} -
  • - {% endfor %} -
+
+ + {% trans "Message reporter" %} + + +
+ {% trans "Update on your report:" as dm_template %} + {% include 'snippets/create_status/status.html' with type="direct" uuid=1 mention=report.reporter prepared_content=dm_template no_script=True %} +
+
+
+ +{% if report.status %} +
+

{% trans "Reported status" %}

+ {% if report.status.deleted %} + {% trans "Status has been deleted" %} + {% else %} + {% include 'snippets/status/status.html' with status=report.status|load_subclass moderation_mode=True %} + {% endif %}
{% endif %} @@ -68,9 +76,13 @@ {% endfor %}
{% csrf_token %} - - - +
+ + +
+
+ +
{% endblock %} diff --git a/bookwyrm/templates/settings/reports/report_filters.html b/bookwyrm/templates/settings/reports/report_filters.html new file mode 100644 index 00000000..48a3b7c8 --- /dev/null +++ b/bookwyrm/templates/settings/reports/report_filters.html @@ -0,0 +1,7 @@ +{% extends 'snippets/filters_panel/filters_panel.html' %} + +{% block filter_fields %} +{% include 'settings/users/username_filter.html' %} +{% include 'directory/community_filter.html' %} +{% include 'settings/users/server_filter.html' %} +{% endblock %} diff --git a/bookwyrm/templates/settings/reports/report_header.html b/bookwyrm/templates/settings/reports/report_header.html index d76db104..878a825d 100644 --- a/bookwyrm/templates/settings/reports/report_header.html +++ b/bookwyrm/templates/settings/reports/report_header.html @@ -1,7 +1,7 @@ {% load i18n %} {% load utilities %} -{% if report.statuses.exists %} +{% if report.status %} {% blocktrans trimmed with report_id=report.id username=report.user|username %} Report #{{ report_id }}: Status posted by @{{ username }} diff --git a/bookwyrm/templates/settings/reports/report_preview.html b/bookwyrm/templates/settings/reports/report_preview.html index 31bde0a1..bd0009c5 100644 --- a/bookwyrm/templates/settings/reports/report_preview.html +++ b/bookwyrm/templates/settings/reports/report_preview.html @@ -4,7 +4,7 @@ {% load utilities %} {% block card-header %} -

+

{% include "settings/reports/report_header.html" with report=report %} diff --git a/bookwyrm/templates/settings/reports/reports.html b/bookwyrm/templates/settings/reports/reports.html index c72fd03d..64db2f26 100644 --- a/bookwyrm/templates/settings/reports/reports.html +++ b/bookwyrm/templates/settings/reports/reports.html @@ -30,7 +30,7 @@

-{% include 'settings/users/user_admin_filters.html' %} +{% include 'settings/reports/report_filters.html' %}
{% if not reports %} @@ -44,5 +44,6 @@ {% endfor %}
+{% include 'snippets/pagination.html' with page=reports path=request.path %} {% endblock %} diff --git a/bookwyrm/templates/settings/site.html b/bookwyrm/templates/settings/site.html index 8efad308..4fd14783 100644 --- a/bookwyrm/templates/settings/site.html +++ b/bookwyrm/templates/settings/site.html @@ -8,14 +8,37 @@ {% block site-subtabs %} {% endblock %} {% block panel %} -
+{% if success %} +
+ + + {% trans "Settings saved" %} + +
+{% endif %} + +{% if site_form.errors %} +
+ + + {% trans "Unable to save settings" %} + +
+{% endif %} + + {% csrf_token %}

{% trans "Instance Info" %}

@@ -50,20 +73,33 @@ -
-

{% trans "Images" %}

-
-
- - {{ site_form.logo }} +
+

{% trans "Display" %}

+
+

{% trans "Images" %}

+
+
+ + {{ site_form.logo }} +
+
+ + {{ site_form.logo_small }} +
+
+ + {{ site_form.favicon }} +
-
- - {{ site_form.logo_small }} -
-
- - {{ site_form.favicon }} + +

{% trans "Themes" %}

+
+ +
+ {{ site_form.default_theme }} +
@@ -103,12 +139,6 @@ {% trans "Allow registration" %}
-
- -

{% trans "(Recommended if registration is open)" %}

+
+ +
+
+ +
+
+ +
{{ site_form.registration_closed_text }} @@ -123,7 +171,7 @@
{{ site_form.invite_request_text }} - + {% include 'snippets/form_errors.html' with errors_list=site_form.invite_request_text.errors id="desc_invite_request_text" %}
diff --git a/bookwyrm/templates/settings/themes.html b/bookwyrm/templates/settings/themes.html new file mode 100644 index 00000000..afa3f0be --- /dev/null +++ b/bookwyrm/templates/settings/themes.html @@ -0,0 +1,122 @@ +{% extends 'settings/layout.html' %} +{% load i18n %} + +{% block title %}{% trans "Themes" %}{% endblock %} + +{% block header %}{% trans "Themes" %}{% endblock %} + +{% block breadcrumbs %} + + {% trans "Set instance default theme" %} + +{% endblock %} + +{% block panel %} +{% if success %} +
+ + + {% trans "Successfully added theme" %} + +
+{% endif %} + +
+
+

{% trans "How to add a theme" %}

+
    +
  1. + {% trans "Copy the theme file into the bookwyrm/static/css/themes directory on your server from the command line." %} +
  2. +
  3. + {% trans "Run ./bw-dev collectstatic." %} +
  4. +
  5. + {% trans "Add the file name using the form below to make it available in the application interface." %} +
  6. +
+
+
+ +
+

{% trans "Add theme" %}

+ + {% if theme_form.errors %} +
+ + + {% trans "Unable to save theme" %} + +
+ {% endif %} + + +
+ {% csrf_token %} +
+
+ +
+ {{ theme_form.name }} + {% include 'snippets/form_errors.html' with errors_list=theme_form.name.errors id="desc_name" %} +
+
+ +
+ +
+ {{ theme_form.path }} + {% include 'snippets/form_errors.html' with errors_list=theme_form.path.errors id="desc_path" %} +
+
+
+ + +
+ +
+ +
+

{% trans "Available Themes" %}

+
+ + + + + + + {% for theme in themes %} + + + + + + {% endfor %} +
+ {% trans "Theme name" %} + + {% trans "File" %} + + {% trans "Actions" %} +
{{ theme.name }}{{ theme.path }} +
+ {% csrf_token %} + +
+
+
+
+ +{% endblock %} diff --git a/bookwyrm/templates/settings/users/email_filter.html b/bookwyrm/templates/settings/users/email_filter.html new file mode 100644 index 00000000..5d49d615 --- /dev/null +++ b/bookwyrm/templates/settings/users/email_filter.html @@ -0,0 +1,16 @@ +{% extends 'snippets/filters_panel/filter_field.html' %} +{% load i18n %} + +{% block filter %} + +
+ +
+{% endblock %} + diff --git a/bookwyrm/templates/settings/users/user.html b/bookwyrm/templates/settings/users/user.html index 676502e6..c18ba829 100644 --- a/bookwyrm/templates/settings/users/user.html +++ b/bookwyrm/templates/settings/users/user.html @@ -1,10 +1,23 @@ {% extends 'settings/layout.html' %} {% load i18n %} +{% load utilities %} {% block title %}{{ user.username }}{% endblock %} {% block header %} {{ user.username }} -{% trans "Back to users" %} +{% endblock %} + +{% block breadcrumbs %} + {% endblock %} {% block panel %} diff --git a/bookwyrm/templates/settings/users/user_admin.html b/bookwyrm/templates/settings/users/user_admin.html index 874ce818..4144f0bd 100644 --- a/bookwyrm/templates/settings/users/user_admin.html +++ b/bookwyrm/templates/settings/users/user_admin.html @@ -1,5 +1,7 @@ {% extends 'settings/layout.html' %} {% load i18n %} +{% load utilities %} + {% block title %}{% trans "Users" %}{% endblock %} {% block header %} @@ -15,46 +17,82 @@ {% include 'settings/users/user_admin_filters.html' %} - - - {% url 'settings-users' as url %} - - - - - - - {% for user in users %} - - - - - - + {% for user in users %} + + + + + + {% if status != "local" %} + + {% endif %} + + {% endfor %} +
- {% trans "Username" as text %} - {% include 'snippets/table-sort-header.html' with field="username" sort=sort text=text %} - - {% trans "Date Added" as text %} - {% include 'snippets/table-sort-header.html' with field="created_date" sort=sort text=text %} - - {% trans "Last Active" as text %} - {% include 'snippets/table-sort-header.html' with field="last_active_date" sort=sort text=text %} - - {% trans "Status" as text %} - {% include 'snippets/table-sort-header.html' with field="is_active" sort=sort text=text %} - - {% trans "Remote instance" as text %} - {% include 'snippets/table-sort-header.html' with field="federated_server__server_name" sort=sort text=text %} -
{{ user.username }}{{ user.created_date }}{{ user.last_active_date }}{% if user.is_active %}{% trans "Active" %}{% else %}{% trans "Inactive" %}{% endif %} - {% if user.federated_server %} - {{ user.federated_server.server_name }} - {% elif not user.local %} - {% trans "Not set" %} +
+
+ +
+
+ +
+ + + {% url 'settings-users' as url %} + + + + + {% if status != "local" %} + {% endif %} - - - {% endfor %} -
+ {% trans "Username" as text %} + {% include 'snippets/table-sort-header.html' with field="username" sort=sort text=text %} + + {% trans "Date Added" as text %} + {% include 'snippets/table-sort-header.html' with field="created_date" sort=sort text=text %} + + {% trans "Last Active" as text %} + {% include 'snippets/table-sort-header.html' with field="last_active_date" sort=sort text=text %} + + {% trans "Status" as text %} + {% include 'snippets/table-sort-header.html' with field="is_active" sort=sort text=text %} + + {% trans "Remote instance" as text %} + {% include 'snippets/table-sort-header.html' with field="federated_server__server_name" sort=sort text=text %} +
+
+ {{ user|username }} + {{ user.created_date }}{{ user.last_active_date }} + {% if user.is_active %} + + {% trans "Active" %} + {% else %} + + {% trans "Inactive" %} + ({{ user.get_deactivation_reason_display }}) + {% endif %} + + {% if user.federated_server %} + {{ user.federated_server.server_name }} + {% else %} + {% trans "Not set" %} + {% endif %} +
+
{% include 'snippets/pagination.html' with page=users path=request.path %} {% endblock %} diff --git a/bookwyrm/templates/settings/users/user_admin_filters.html b/bookwyrm/templates/settings/users/user_admin_filters.html index 48a3b7c8..c5ab2dab 100644 --- a/bookwyrm/templates/settings/users/user_admin_filters.html +++ b/bookwyrm/templates/settings/users/user_admin_filters.html @@ -2,6 +2,11 @@ {% block filter_fields %} {% include 'settings/users/username_filter.html' %} -{% include 'directory/community_filter.html' %} + +{% if status != "local" %} {% include 'settings/users/server_filter.html' %} +{% else %} +{% include 'settings/users/email_filter.html' %} +{% endif %} + {% endblock %} diff --git a/bookwyrm/templates/settings/users/user_info.html b/bookwyrm/templates/settings/users/user_info.html index 8d332b1a..a04db3b8 100644 --- a/bookwyrm/templates/settings/users/user_info.html +++ b/bookwyrm/templates/settings/users/user_info.html @@ -6,14 +6,18 @@

{% trans "Profile" %}

- {% include 'user/user_preview.html' with user=user %} + {% include 'user/user_preview.html' with user=user admin_mode=True %} {% if user.summary %} -
+
{{ user.summary|to_markdown|safe }}
{% endif %}

{% trans "View user profile" %}

+ {% url 'settings-user' user.id as url %} + {% if not request.path == url %} +

{% trans "Go to user admin" %}

+ {% endif %}
@@ -67,18 +71,21 @@
{% trans "Blocked by count:" %}
{{ user.blocked_by.count }}
+
{% trans "Date added:" %}
+
{{ user.created_date }}
+
{% trans "Last active date:" %}
{{ user.last_active_date }}
{% trans "Manually approved followers:" %}
-
{{ user.manually_approves_followers }}
+
{{ user.manually_approves_followers|yesno }}
{% trans "Discoverable:" %}
-
{{ user.discoverable }}
+
{{ user.discoverable|yesno }}
{% if not user.is_active %}
{% trans "Deactivation reason:" %}
-
{{ user.deactivation_reason }}
+
{{ user.get_deactivation_reason_display }}
{% endif %} {% if not user.is_active and user.deactivation_reason == "pending" %} @@ -104,11 +111,11 @@
{{ server.application_version }}
{% trans "Status:" %}
-
{{ server.status }}
+
{{ server.get_status_display }}
{% if server.notes %}
{% trans "Notes" %}
-
+
{{ server.notes }}
{% endif %} diff --git a/bookwyrm/templates/setup/admin.html b/bookwyrm/templates/setup/admin.html new file mode 100644 index 00000000..ca6cae73 --- /dev/null +++ b/bookwyrm/templates/setup/admin.html @@ -0,0 +1,61 @@ +{% extends 'setup/layout.html' %} +{% load i18n %} + +{% block header %} +

{% trans "Set up BookWyrm" %}

+
+ {% trans "Your account as a user and an admin" %} +
+{% endblock %} + +{% block panel %} +
+

{% trans "Create your account" %}

+
+
+
+
+
+ +
+ +

+ {% blocktrans trimmed %} + An admin key was created when you installed BookWyrm. + You can get your admin key by running ./bw-dev admin_code from the command line on your server. + {% endblocktrans %} +

+
+
+ {% include 'snippets/register_form.html' %} +
+
+
+
+

+ {% blocktrans trimmed %} + As an admin, you'll be able to configure the instance name and information, and moderate your instance. + This means you will have access to private information about your users, and are responsible for responding to reports of bad behavior or spam. + {% endblocktrans %} +

+

+ {% trans "Once the instance is set up, you can promote other users to moderator or admin roles from the admin panel." %} +

+

+ + {% trans "Learn more about moderation" %} + +

+
+
+
+{% endblock %} diff --git a/bookwyrm/templates/setup/config.html b/bookwyrm/templates/setup/config.html new file mode 100644 index 00000000..b14ad37d --- /dev/null +++ b/bookwyrm/templates/setup/config.html @@ -0,0 +1,154 @@ +{% extends 'setup/layout.html' %} +{% load i18n %} + +{% block header %} +

{% trans "Instance Configuration" %}

+
+ {% trans "Make sure everything looks right before proceeding" %} +
+{% endblock %} + +{% block panel %} + +
+ {% if warnings.debug %} +
+ + + {% blocktrans trimmed %} + You are running BookWyrm in debug mode. + This should never be used in a production environment. + {% endblocktrans %} + +
+ {% endif %} + + {% if warnings.invalid_domain %} +
+ + + {% blocktrans trimmed %} + Your domain appears to be misconfigured. + It should not include protocol or slashes. + {% endblocktrans %} + +
+ {% endif %} + + {% if warnings.protocol %} +
+ + + {% blocktrans trimmed %} + You are running BookWyrm in production mode without https. + USE_HTTPS should be enabled in production. + {% endblocktrans %} + +
+ {% endif %} + +
+
+

{% trans "Settings" %}

+
+
+
+ {% trans "Instance domain:" %} +
+
+ {{ info.domain }} +
+ +
+ {% trans "Protocol:" %} +
+
+ {% if info.use_https %} + https + {% else %} + http + {% endif %} +
+ +
+ {% trans "Software version:" %} +
+
+ {{ info.version }} +
+ +
+ {% trans "Using S3:" %} +
+
+ {{ info.use_s3|yesno }} +
+
+
+
+ +
+

{% trans "Display" %}

+
+
+
+ {% trans "Default interface language:" %} +
+
+ {{ info.language }} +
+ +
+ {% trans "Email sender:" %} +
+
+ {{ info.email_sender }} +
+ +
+ {% trans "Enable preview images:" %} +
+
+ {{ info.preview_images|yesno }} +
+ +
+ {% trans "Enable image thumbnails:" %} +
+
+ {{ info.thumbnails|yesno }} +
+
+
+
+
+
+ +
+

{% trans "Does everything look right?" %}

+

+ {% blocktrans trimmed %} + This is your last chance to set your domain and protocol. + {% endblocktrans %} +

+ +
+ + +

+ {% blocktrans trimmed %} + You can change your instance settings in the .env file on your server. + {% endblocktrans %} + + {% trans "View installation instructions" %} + +

+
+
+ +{% endblock %} diff --git a/bookwyrm/templates/setup/layout.html b/bookwyrm/templates/setup/layout.html new file mode 100644 index 00000000..8dca1be2 --- /dev/null +++ b/bookwyrm/templates/setup/layout.html @@ -0,0 +1,37 @@ +{% extends 'layout.html' %} +{% load i18n %} +{% load static %} + +{% block title %}{% trans "Instance Setup" %}{% endblock %} + +{% block body %} + + + +
+
+
+ {% block header %}{% endblock %} +
+ +
+ {% block panel %}{% endblock %} +
+
+
+ +{% endblock %} diff --git a/bookwyrm/templates/shelf/create_shelf_form.html b/bookwyrm/templates/shelf/create_shelf_form.html index c3d2b5fa..3bf17199 100644 --- a/bookwyrm/templates/shelf/create_shelf_form.html +++ b/bookwyrm/templates/shelf/create_shelf_form.html @@ -2,7 +2,7 @@ {% load i18n %} {% block header %} -{% trans "Create Shelf" %} +{% trans "Create shelf" %} {% endblock %} {% block form %} diff --git a/bookwyrm/templates/shelf/form.html b/bookwyrm/templates/shelf/form.html index ff7f8b5e..a2533010 100644 --- a/bookwyrm/templates/shelf/form.html +++ b/bookwyrm/templates/shelf/form.html @@ -17,7 +17,7 @@
-
+
{% include 'snippets/privacy_select.html' with current=privacy %}
diff --git a/bookwyrm/templates/shelf/shelf.html b/bookwyrm/templates/shelf/shelf.html index 3a565221..cc4bb143 100644 --- a/bookwyrm/templates/shelf/shelf.html +++ b/bookwyrm/templates/shelf/shelf.html @@ -45,7 +45,7 @@ href="{{ shelf_tab.local_path }}" {% if shelf_tab.identifier == shelf.identifier %} aria-current="page"{% endif %} > - {% include 'user/books_header.html' with shelf=shelf_tab %} + {% include "snippets/translated_shelf_name.html" with shelf=shelf_tab %} {% endfor %} diff --git a/bookwyrm/templates/snippets/announcement.html b/bookwyrm/templates/snippets/announcement.html index 8b856319..d9da01c9 100644 --- a/bookwyrm/templates/snippets/announcement.html +++ b/bookwyrm/templates/snippets/announcement.html @@ -1,32 +1,29 @@ {% load humanize %}{% load i18n %}{% load utilities %} {% with announcement.id|uuid as uuid %}