Merge branch 'upstream' into tab-keyboard-accessibility

This commit is contained in:
Ned Zimmerman 2021-02-28 12:39:56 -04:00
commit b324e668ca
104 changed files with 2680 additions and 690 deletions

View file

@ -26,16 +26,19 @@ There are many ways you can contribute to this project, regardless of your level
Please feel encouraged and welcome to point out bugs, suggestions, feature requests, and ideas for how things ought to work using [GitHub issues](https://github.com/mouse-reeve/bookwyrm/issues). Please feel encouraged and welcome to point out bugs, suggestions, feature requests, and ideas for how things ought to work using [GitHub issues](https://github.com/mouse-reeve/bookwyrm/issues).
### Code contributions ### Code contributions
Code contributons are gladly welcomed! If you're not sure where to start, take a look at the ["Good first issue"](https://github.com/mouse-reeve/bookwyrm/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. Because BookWyrm is a small project, there isn't a lot of formal structure, but there is a huge capacity for one-on-one support, which can look like asking questions as you go, pair programming, video chats, et cetera, so please feel free to reach out. Code contributions are gladly welcomed! If you're not sure where to start, take a look at the ["Good first issue"](https://github.com/mouse-reeve/bookwyrm/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. Because BookWyrm is a small project, there isn't a lot of formal structure, but there is a huge capacity for one-on-one support, which can look like asking questions as you go, pair programming, video chats, et cetera, so please feel free to reach out.
If you have questions about the project or contributing, you can seet up a video call during BookWyrm ["office hours"](https://calendly.com/mouse-reeve/30min). If you have questions about the project or contributing, you can set up a video call during BookWyrm ["office hours"](https://calendly.com/mouse-reeve/30min).
### Translation
Do you speak a language besides English? BookWyrm needs localization! If you're comfortable using git and want to get into the code, there are [instructions](#workin-with-translations-and-locale-files) on how to create and edit localization files. If you feel more comfortable working in a regular text editor and would prefer not to run the application, get in touch directly and we can figure out a system, like emailing a text file, that works best.
### Financial Support ### Financial Support
BookWyrm is an ad-free passion project with no intentions of seeking out venture funding or corporate financial relationships. If you want to help keep the project going, you can donate to the [Patreon](https://www.patreon.com/bookwyrm), or make a one time gift via [PayPal](https://paypal.me/oulipo). BookWyrm is an ad-free passion project with no intentions of seeking out venture funding or corporate financial relationships. If you want to help keep the project going, you can donate to the [Patreon](https://www.patreon.com/bookwyrm), or make a one time gift via [PayPal](https://paypal.me/oulipo).
## About BookWyrm ## About BookWyrm
### What it is and isn't ### What it is and isn't
BookWyrm is a platform for social reading! You can use it to track what you're reading, review books, and follow your friends. It isn't primarily meant for cataloguing or as a datasource for books, but it does do both of those things to some degree. BookWyrm is a platform for social reading! You can use it to track what you're reading, review books, and follow your friends. It isn't primarily meant for cataloguing or as a data-source for books, but it does do both of those things to some degree.
### The role of federation ### The role of federation
BookWyrm is built on [ActivityPub](http://activitypub.rocks/). With ActivityPub, it inter-operates with different instances of BookWyrm, and other ActivityPub compliant services, like Mastodon. This means you can run an instance for your book club, and still follow your friend who posts on a server devoted to 20th century Russian speculative fiction. It also means that your friend on mastodon can read and comment on a book review that you post on your BookWyrm instance. BookWyrm is built on [ActivityPub](http://activitypub.rocks/). With ActivityPub, it inter-operates with different instances of BookWyrm, and other ActivityPub compliant services, like Mastodon. This means you can run an instance for your book club, and still follow your friend who posts on a server devoted to 20th century Russian speculative fiction. It also means that your friend on mastodon can read and comment on a book review that you post on your BookWyrm instance.
@ -109,19 +112,46 @@ docker-compose up
Once the build is complete, you can access the instance at `localhost:1333` Once the build is complete, you can access the instance at `localhost:1333`
### Editing static files
If you edit the CSS or JavaScript, you will need to run Django's `collectstatic` command in order for your changes to have effect. You can do this by running:
``` bash
./bw-dev collectstatic
```
### Workin with translations and locale files
Text in the html files are wrapped in translation tags (`{% trans %}` and `{% blocktrans %}`), and Django generates locale files for all the strings in which you can add translations for the text. You can find existing translations in the `locale/` directory.
The application's language is set by a request header sent by your browser to the application, so to change the language of the application, you can change the default language requested by your browser.
#### Adding a locale
To start translation into a language which is currently supported, run the django-admin `makemessages` command with the language code for the language you want to add (like `de` for German, or `en-gb` for British English):
``` bash
./bw-dev makemessages -l <language code>
```
#### Editing a locale
When you have a locale file, open the `django.po` in the directory for the language (for example, if you were adding German, `locale/de/LC_MESSAGES/django.po`. All the the text in the application will be shown in paired strings, with `msgid` as the original text, and `msgstr` as the translation (by default, this is set to an empty string, and will display the original text).
Add you translations to the `msgstr` strings, and when you're ready, compile the locale by running:
``` bash
./bw-dev compilemessages
```
You can add the `-l <language code>` to only compile one language. When you refresh the application, you should see your translations at work.
## Installing in Production ## Installing in Production
This project is still young and isn't, at the momoment, very stable, so please procede with caution when running in production. This project is still young and isn't, at the moment, very stable, so please proceed with caution when running in production.
### Server setup ### Server setup
- Get a domain name and set up DNS for your server - Get a domain name and set up DNS for your server
- Set your server up with appropriate firewalls for running a web application (this instruction set is tested again Ubuntu 20.04) - Set your server up with appropriate firewalls for running a web application (this instruction set is tested against Ubuntu 20.04)
- Set up an email service (such as mailgun) and the appropriate SMTP/DNS settings - Set up an email service (such as mailgun) and the appropriate SMTP/DNS settings
- Install Docker and docker-compose - Install Docker and docker-compose
### Install and configure BookWyrm ### Install and configure BookWyrm
The `production` branch of BookWyrm contains a number of tools not on the `main` branch that are suited for running in production, such as `docker-compose` changes to update the default commands or configuration of containers, and indivudal changes to container config to enable things like SSL or regular backups. The `production` branch of BookWyrm contains a number of tools not on the `main` branch that are suited for running in production, such as `docker-compose` changes to update the default commands or configuration of containers, and individual changes to container config to enable things like SSL or regular backups.
Instructions for running BookWyrm in production: Instructions for running BookWyrm in production:
@ -171,3 +201,4 @@ There are three concepts in the book data model:
- `Edition`, a concrete, actually published version of a book - `Edition`, a concrete, actually published version of a book
Whenever a user interacts with a book, they are interacting with a specific edition. Every work has a default edition, but the user can select other editions. Reviews aggregated for all editions of a work when you view an edition's page. Whenever a user interacts with a book, they are interacting with a specific edition. Every work has a default edition, but the user can select other editions. Reviews aggregated for all editions of a work when you view an edition's page.

View file

@ -1,121 +1,13 @@
''' handle reading a csv from goodreads ''' ''' handle reading a csv from goodreads '''
import csv from bookwyrm.importer import Importer
import logging
from bookwyrm import models # GoodReads is the default importer, thus Importer follows its structure. For a more complete example of overriding see librarything_import.py
from bookwyrm.models import ImportJob, ImportItem
from bookwyrm.tasks import app
logger = logging.getLogger(__name__) class GoodreadsImporter(Importer):
service = 'GoodReads'
def parse_fields(self, data):
def create_job(user, csv_file, include_reviews, privacy): data.update({'import_source': self.service })
''' check over a csv and creates a database entry for the job''' # add missing 'Date Started' field
job = ImportJob.objects.create( data.update({'Date Started': None })
user=user, return data
include_reviews=include_reviews,
privacy=privacy
)
for index, entry in enumerate(list(csv.DictReader(csv_file))):
if not all(x in entry for x in ('ISBN13', 'Title', 'Author')):
raise ValueError('Author, title, and isbn must be in data.')
ImportItem(job=job, index=index, data=entry).save()
return job
def create_retry_job(user, original_job, items):
''' retry items that didn't import '''
job = ImportJob.objects.create(
user=user,
include_reviews=original_job.include_reviews,
privacy=original_job.privacy,
retry=True
)
for item in items:
ImportItem(job=job, index=item.index, data=item.data).save()
return job
def start_import(job):
''' initalizes a csv import job '''
result = import_data.delay(job.id)
job.task_id = result.id
job.save()
@app.task
def import_data(job_id):
''' does the actual lookup work in a celery task '''
job = ImportJob.objects.get(id=job_id)
try:
for item in job.items.all():
try:
item.resolve()
except Exception as e:# pylint: disable=broad-except
logger.exception(e)
item.fail_reason = 'Error loading book'
item.save()
continue
if item.book:
item.save()
# shelves book and handles reviews
handle_imported_book(
job.user, item, job.include_reviews, job.privacy)
else:
item.fail_reason = 'Could not find a match for book'
item.save()
finally:
job.complete = True
job.save()
def handle_imported_book(user, item, include_reviews, privacy):
''' process a goodreads csv and then post about it '''
if isinstance(item.book, models.Work):
item.book = item.book.default_edition
if not item.book:
return
existing_shelf = models.ShelfBook.objects.filter(
book=item.book, user=user).exists()
# shelve the book if it hasn't been shelved already
if item.shelf and not existing_shelf:
desired_shelf = models.Shelf.objects.get(
identifier=item.shelf,
user=user
)
models.ShelfBook.objects.create(
book=item.book, shelf=desired_shelf, user=user)
for read in item.reads:
# check for an existing readthrough with the same dates
if models.ReadThrough.objects.filter(
user=user, book=item.book,
start_date=read.start_date,
finish_date=read.finish_date
).exists():
continue
read.book = item.book
read.user = user
read.save()
if include_reviews and (item.rating or item.review):
review_title = 'Review of {!r} on Goodreads'.format(
item.book.title,
) if item.review else ''
# we don't know the publication date of the review,
# but "now" is a bad guess
published_date_guess = item.date_read or item.date_added
models.Review.objects.create(
user=user,
book=item.book,
name=review_title,
content=item.review,
rating=item.rating,
published_date=published_date_guess,
privacy=privacy,
)

135
bookwyrm/importer.py Normal file
View file

@ -0,0 +1,135 @@
''' handle reading a csv from an external service, defaults are from GoodReads '''
import csv
import logging
from bookwyrm import models
from bookwyrm.models import ImportJob, ImportItem
from bookwyrm.tasks import app
logger = logging.getLogger(__name__)
class Importer:
service = 'Unknown'
delimiter = ','
encoding = 'UTF-8'
mandatory_fields = ['Title', 'Author']
def create_job(self, user, csv_file, include_reviews, privacy):
''' check over a csv and creates a database entry for the job'''
job = ImportJob.objects.create(
user=user,
include_reviews=include_reviews,
privacy=privacy
)
for index, entry in enumerate(list(csv.DictReader(csv_file, delimiter=self.delimiter ))):
if not all(x in entry for x in self.mandatory_fields):
raise ValueError('Author and title must be in data.')
entry = self.parse_fields(entry)
self.save_item(job, index, entry)
return job
def save_item(self, job, index, data):
ImportItem(job=job, index=index, data=data).save()
def parse_fields(self, entry):
entry.update({'import_source': self.service })
return entry
def create_retry_job(self, user, original_job, items):
''' retry items that didn't import '''
job = ImportJob.objects.create(
user=user,
include_reviews=original_job.include_reviews,
privacy=original_job.privacy,
retry=True
)
for item in items:
self.save_item(job, item.index, item.data)
return job
def start_import(self, job):
''' initalizes a csv import job '''
result = import_data.delay(self.service, job.id)
job.task_id = result.id
job.save()
@app.task
def import_data(source, job_id):
''' does the actual lookup work in a celery task '''
job = ImportJob.objects.get(id=job_id)
try:
for item in job.items.all():
try:
item.resolve()
except Exception as e:# pylint: disable=broad-except
logger.exception(e)
item.fail_reason = 'Error loading book'
item.save()
continue
if item.book:
item.save()
# shelves book and handles reviews
handle_imported_book(source,
job.user, item, job.include_reviews, job.privacy)
else:
item.fail_reason = 'Could not find a match for book'
item.save()
finally:
job.complete = True
job.save()
def handle_imported_book(source, user, item, include_reviews, privacy):
''' process a csv and then post about it '''
if isinstance(item.book, models.Work):
item.book = item.book.default_edition
if not item.book:
return
existing_shelf = models.ShelfBook.objects.filter(
book=item.book, user=user).exists()
# shelve the book if it hasn't been shelved already
if item.shelf and not existing_shelf:
desired_shelf = models.Shelf.objects.get(
identifier=item.shelf,
user=user
)
models.ShelfBook.objects.create(
book=item.book, shelf=desired_shelf, user=user)
for read in item.reads:
# check for an existing readthrough with the same dates
if models.ReadThrough.objects.filter(
user=user, book=item.book,
start_date=read.start_date,
finish_date=read.finish_date
).exists():
continue
read.book = item.book
read.user = user
read.save()
if include_reviews and (item.rating or item.review):
review_title = 'Review of {!r} on {!r}'.format(
item.book.title,
source,
) if item.review else ''
# we don't know the publication date of the review,
# but "now" is a bad guess
published_date_guess = item.date_read or item.date_added
models.Review.objects.create(
user=user,
book=item.book,
name=review_title,
content=item.review,
rating=item.rating,
published_date=published_date_guess,
privacy=privacy,
)

View file

@ -0,0 +1,42 @@
''' handle reading a csv from librarything '''
import csv
import re
import math
from bookwyrm import models
from bookwyrm.models import ImportItem
from bookwyrm.importer import Importer
class LibrarythingImporter(Importer):
service = 'LibraryThing'
delimiter = '\t'
encoding = 'ISO-8859-1'
# mandatory_fields : fields matching the book title and author
mandatory_fields = ['Title', 'Primary Author']
def parse_fields(self, initial):
data = {}
data['import_source'] = self.service
data['Book Id'] = initial['Book Id']
data['Title'] = initial['Title']
data['Author'] = initial['Primary Author']
data['ISBN13'] = initial['ISBN']
data['My Review'] = initial['Review']
if initial['Rating']:
data['My Rating'] = math.ceil(float(initial['Rating']))
else:
data['My Rating'] = ''
data['Date Added'] = re.sub('\[|\]', '', initial['Entry Date'])
data['Date Started'] = re.sub('\[|\]', '', initial['Date Started'])
data['Date Read'] = re.sub('\[|\]', '', initial['Date Read'])
data['Exclusive Shelf'] = None
if data['Date Read']:
data['Exclusive Shelf'] = "read"
elif data['Date Started']:
data['Exclusive Shelf'] = "reading"
else:
data['Exclusive Shelf'] = "to-read"
return data

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.7 on 2021-02-27 19:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bookwyrm', '0045_auto_20210210_2114'),
]
operations = [
migrations.AddField(
model_name='sitesettings',
name='privacy_policy',
field=models.TextField(default='Add a privacy policy here.'),
),
]

View file

@ -97,8 +97,8 @@ class ImportItem(models.Model):
def get_book_from_title_author(self): def get_book_from_title_author(self):
''' search by title and author ''' ''' search by title and author '''
search_term = construct_search_term( search_term = construct_search_term(
self.data['Title'], self.title,
self.data['Author'] self.author
) )
search_result = connector_manager.first_search_result( search_result = connector_manager.first_search_result(
search_term, min_confidence=0.999 search_term, min_confidence=0.999
@ -149,6 +149,14 @@ class ImportItem(models.Model):
dateutil.parser.parse(self.data['Date Added'])) dateutil.parser.parse(self.data['Date Added']))
return None return None
@property
def date_started(self):
''' when the book was started '''
if "Date Started" in self.data and self.data['Date Started']:
return timezone.make_aware(
dateutil.parser.parse(self.data['Date Started']))
return None
@property @property
def date_read(self): def date_read(self):
''' the date a book was completed ''' ''' the date a book was completed '''
@ -160,18 +168,24 @@ class ImportItem(models.Model):
@property @property
def reads(self): def reads(self):
''' formats a read through dataset for the book in this line ''' ''' formats a read through dataset for the book in this line '''
if (self.shelf == 'reading' start_date = self.date_started
and self.date_added and not self.date_read):
return [ReadThrough(start_date=self.date_added)] # Goodreads special case (no 'date started' field)
if ((self.shelf == 'reading' or (self.shelf == 'read' and self.date_read))
and self.date_added and not start_date):
start_date = self.date_added
if (start_date and start_date is not None and not self.date_read):
return [ReadThrough(start_date=start_date)]
if self.date_read: if self.date_read:
return [ReadThrough( return [ReadThrough(
start_date=self.date_added, start_date=start_date,
finish_date=self.date_read, finish_date=self.date_read,
)] )]
return [] return []
def __repr__(self): def __repr__(self):
return "<GoodreadsItem {!r}>".format(self.data['Title']) return "<{!r}Item {!r}>".format(self.data['import_source'], self.data['Title'])
def __str__(self): def __str__(self):
return "{} by {}".format(self.data['Title'], self.data['Author']) return "{} by {}".format(self.data['Title'], self.data['Author'])

View file

@ -20,6 +20,8 @@ class SiteSettings(models.Model):
default='Contact an administrator to get an invite') default='Contact an administrator to get an invite')
code_of_conduct = models.TextField( code_of_conduct = models.TextField(
default='Add a code of conduct here.') default='Add a code of conduct here.')
privacy_policy = models.TextField(
default='Add a privacy policy here.')
allow_registration = models.BooleanField(default=True) allow_registration = models.BooleanField(default=True)
logo = models.ImageField( logo = models.ImageField(
upload_to='logos/', null=True, blank=True upload_to='logos/', null=True, blank=True

View file

@ -1,9 +1,10 @@
''' bookwyrm settings and configuration ''' ''' bookwyrm settings and configuration '''
import os import os
from environs import Env from environs import Env
import requests import requests
from django.utils.translation import gettext_lazy as _
env = Env() env = Env()
DOMAIN = env('DOMAIN') DOMAIN = env('DOMAIN')
@ -27,6 +28,7 @@ EMAIL_USE_TLS = env('EMAIL_USE_TLS', True)
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'),]
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
@ -58,6 +60,7 @@ INSTALLED_APPS = [
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -135,6 +138,10 @@ AUTH_PASSWORD_VALIDATORS = [
# https://docs.djangoproject.com/en/2.0/topics/i18n/ # https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
LANGUAGES = [
('en-us', _('English')),
]
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
@ -9,8 +10,8 @@
{% if request.user.is_authenticated and perms.bookwyrm.edit_book %} {% if request.user.is_authenticated and perms.bookwyrm.edit_book %}
<div class="column is-narrow"> <div class="column is-narrow">
<a href="{{ author.local_path }}/edit"> <a href="{{ author.local_path }}/edit">
<span class="icon icon-pencil" title="Edit Author"> <span class="icon icon-pencil" title="{% trans 'Edit Author' %}">
<span class="is-sr-only">Edit Author</span> <span class="is-sr-only">{% trans "Edit Author" %}</span>
</span> </span>
</a> </a>
</div> </div>
@ -25,12 +26,12 @@
</p> </p>
{% endif %} {% endif %}
{% if author.wikipedia_link %} {% if author.wikipedia_link %}
<p><a href="{{ author.wikipedia_link }}" rel=”noopener” target="_blank">Wikipedia</a></p> <p><a href="{{ author.wikipedia_link }}" rel=”noopener” target="_blank">{% trans "Wikipedia" %}</a></p>
{% endif %} {% endif %}
</div> </div>
<div class="block"> <div class="block">
<h3 class="title is-4">Books by {{ author.name }}</h3> <h3 class="title is-4">{% blocktrans with name=author.name %}Books by {{ name }}{% endblocktrans %}</h3>
{% include 'snippets/book_tiles.html' with books=books %} {% include 'snippets/book_tiles.html' with books=books %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load humanize %} {% load humanize %}
{% block content %} {% block content %}
@ -23,8 +24,8 @@
{% if request.user.is_authenticated and perms.bookwyrm.edit_book %} {% if request.user.is_authenticated and perms.bookwyrm.edit_book %}
<div class="column is-narrow"> <div class="column is-narrow">
<a href="{{ book.id }}/edit"> <a href="{{ book.id }}/edit">
<span class="icon icon-pencil" title="Edit Book"> <span class="icon icon-pencil" title="{% trans "Edit Book" %}">
<span class="is-sr-only">Edit Book</span> <span class="is-sr-only">{% trans "Edit Book" %}</span>
</span> </span>
</a> </a>
</div> </div>
@ -39,13 +40,13 @@
{% if request.user.is_authenticated and not book.cover %} {% if request.user.is_authenticated and not book.cover %}
<div class="box p-2"> <div class="box p-2">
<h3 class="title is-6 mb-1">Add cover</h3> <h3 class="title is-6 mb-1">{% trans "Add cover" %}</h3>
<form name="add-cover" method="POST" action="/upload-cover/{{ book.id }}" enctype="multipart/form-data"> <form name="add-cover" method="POST" action="/upload-cover/{{ book.id }}" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<label class="label"> <label class="label">
<input type="file" name="cover" accept="image/*" enctype="multipart/form-data" id="id_cover" required> <input type="file" name="cover" accept="image/*" enctype="multipart/form-data" id="id_cover" required>
</label> </label>
<button class="button is-small is-primary" type="submit">Add</button> <button class="button is-small is-primary" type="submit">{% trans "Add" %}</button>
</form> </form>
</div> </div>
{% endif %} {% endif %}
@ -54,21 +55,21 @@
<dl> <dl>
{% if book.isbn_13 %} {% if book.isbn_13 %}
<div class="is-flex is-justify-content-space-between"> <div class="is-flex is-justify-content-space-between">
<dt>ISBN:</dt> <dt>{% trans "ISBN:" %}</dt>
<dd>{{ book.isbn_13 }}</dd> <dd>{{ book.isbn_13 }}</dd>
</div> </div>
{% endif %} {% endif %}
{% if book.oclc_number %} {% if book.oclc_number %}
<div class="is-flex is-justify-content-space-between"> <div class="is-flex is-justify-content-space-between">
<dt>OCLC Number:</dt> <dt>{% trans "OCLC Number:" %}</dt>
<dd>{{ book.oclc_number }}</dd> <dd>{{ book.oclc_number }}</dd>
</div> </div>
{% endif %} {% endif %}
{% if book.asin %} {% if book.asin %}
<div class="is-flex is-justify-content-space-between"> <div class="is-flex is-justify-content-space-between">
<dt>ASIN:</dt> <dt>{% trans "ASIN:" %}</dt>
<dd>{{ book.asin }}</dd> <dd>{{ book.asin }}</dd>
</div> </div>
{% endif %} {% endif %}
@ -80,7 +81,7 @@
</p> </p>
{% if book.openlibrary_key %} {% if book.openlibrary_key %}
<p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">View on OpenLibrary</a></p> <p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">{% trans "View on OpenLibrary" %}</a></p>
{% endif %} {% endif %}
</section> </section>
</div> </div>
@ -98,11 +99,11 @@
<form name="add-description" method="POST" action="/add-description/{{ book.id }}"> <form name="add-description" method="POST" action="/add-description/{{ book.id }}">
{% csrf_token %} {% csrf_token %}
<p class="fields is-grouped"> <p class="fields is-grouped">
<label class="label"for="id_description">Description:</label> <label class="label"for="id_description">{% trans "Description:" %}</label>
<textarea name="description" cols="None" rows="None" class="textarea" id="id_description"></textarea> <textarea name="description" cols="None" rows="None" class="textarea" id="id_description"></textarea>
</p> </p>
<div class="field"> <div class="field">
<button class="button is-primary" type="submit">Save</button> <button class="button is-primary" type="submit">{% trans "Save" %}</button>
{% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="add-description" controls_uid=book.id hide_inactive=True %} {% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="add-description" controls_uid=book.id hide_inactive=True %}
</div> </div>
</form> </form>
@ -134,20 +135,20 @@
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<section class="block"> <section class="block">
<header class="columns"> <header class="columns">
<h2 class="column title is-5 mb-1">Your reading activity</h2> <h2 class="column title is-5 mb-1">{% trans "Your reading activity" %}</h2>
<div class="column is-narrow"> <div class="column is-narrow">
{% include 'snippets/toggle/open_button.html' with text="Add read dates" icon="plus" class="is-small" controls_text="add-readthrough" %} {% include 'snippets/toggle/open_button.html' with text="Add read dates" icon="plus" class="is-small" controls_text="add-readthrough" %}
</div> </div>
</header> </header>
{% if not readthroughs.exists %} {% if not readthroughs.exists %}
<p>You don't have any reading activity for this book.</p> <p>{% trans "You don't have any reading activity for this book." %}</p>
{% endif %} {% endif %}
<section class="hidden box" id="add-readthrough"> <section class="hidden box" id="add-readthrough">
<form name="add-readthrough" action="/create-readthrough" method="post"> <form name="add-readthrough" action="/create-readthrough" method="post">
{% include 'snippets/readthrough_form.html' with readthrough=None %} {% include 'snippets/readthrough_form.html' with readthrough=None %}
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-primary" type="submit">Create</button> <button class="button is-primary" type="submit">{% trans "Create" %}</button>
</div> </div>
<div class="control"> <div class="control">
{% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="add-readthrough" %} {% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="add-readthrough" %}
@ -168,11 +169,11 @@
<section class="block"> <section class="block">
<form name="tag" action="/tag/" method="post"> <form name="tag" action="/tag/" method="post">
<label for="tags" class="is-3">Tags</label> <label for="tags" class="is-3">{% trans "Tags" %}</label>
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}"> <input type="hidden" name="book" value="{{ book.id }}">
<input id="tags" class="input" type="text" name="name"> <input id="tags" class="input" type="text" name="name">
<button class="button" type="submit">Add tag</button> <button class="button" type="submit">{% trans "Add tag" %}</button>
</form> </form>
</section> </section>
{% endif %} {% endif %}
@ -189,7 +190,7 @@
<div class="column is-narrow"> <div class="column is-narrow">
{% if book.subjects %} {% if book.subjects %}
<section class="content block"> <section class="content block">
<h2 class="title is-5">Subjects</h2> <h2 class="title is-5">{% trans "Subjects" %}</h2>
<ul> <ul>
{% for subject in book.subjects %} {% for subject in book.subjects %}
<li>{{ subject }}</li> <li>{{ subject }}</li>
@ -200,7 +201,7 @@
{% if book.subject_places %} {% if book.subject_places %}
<section class="content block"> <section class="content block">
<h2 class="title is-5">Places</h2> <h2 class="title is-5">{% trans "Places" %}</h2>
<ul> <ul>
{% for place in book.subject_placess %} {% for place in book.subject_placess %}
<li>{{ place }}</li> <li>{{ place }}</li>
@ -211,7 +212,7 @@
{% if lists.exists %} {% if lists.exists %}
<section class="content block"> <section class="content block">
<h2 class="title is-5">Lists</h2> <h2 class="title is-5">{% trans "Lists" %}</h2>
<ul> <ul>
{% for list in lists %} {% for list in lists %}
<li><a href="{{ list.local_path }}">{{ list.name }}</a></li> <li><a href="{{ list.local_path }}">{{ list.name }}</a></li>
@ -240,7 +241,7 @@
{% include 'snippets/username.html' with user=rating.user %} {% include 'snippets/username.html' with user=rating.user %}
</div> </div>
<div class="field is-grouped mb-0"> <div class="field is-grouped mb-0">
<div>rated it</div> <div>{% trans "rated it" %}</div>
{% include 'snippets/stars.html' with rating=rating.rating %} {% include 'snippets/stars.html' with rating=rating.rating %}
</div> </div>
<div> <div>
@ -257,4 +258,3 @@
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,22 +1,37 @@
{% extends 'layout.html' %} {% extends 'discover/landing_layout.html' %}
{% block content %} {% load i18n %}
{% block panel %}
<header class="block has-text-centered"> <div class="block columns mt-4">
<h1 class="title">{{ site.name }}</h1> <nav class="menu column is-one-quarter">
<h2 class="subtitle">{{ site.instance_tagline }}</h2> <h2 class="menu-label">About {{ site.name }}</h2>
</header> <ul class="menu-list">
<li>
<a href="#coc">{% trans "Code of Conduct" %}</a>
</li>
<li>
<a href="#privacy">{% trans "Privacy Policy" %}</a>
</li>
</ul>
</nav>
{% include 'discover/icons.html' %} <div class="column content">
<div class="block" id="coc">
<section class="block"> <h2 class="title">{% trans "Code of Conduct" %}</h2>
{% include 'snippets/about.html' %}
</section>
<div class="block">
<h2 class="title">Code of Conduct</h2>
<div class="content"> <div class="content">
{{ site.code_of_conduct | safe }} {{ site.code_of_conduct | safe }}
</div> </div>
</div>
<hr aria-hidden="true">
<div class="block" id="privacy">
<h2 class="title">{% trans "Privacy Policy" %}</h2>
<div class="content">
{{ site.privacy_policy | safe }}
</div>
</div>
</div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,39 +1,9 @@
{% extends 'layout.html' %} {% extends 'discover/landing_layout.html' %}
{% block content %} {% load i18n %}
{% block panel %}
<header class="block has-text-centered">
<h1 class="title">{{ site.name }}</h1>
<h2 class="subtitle">{{ site.instance_tagline }}</h2>
</header>
{% include 'discover/icons.html' %}
{% if not request.user.is_authenticated %}
<section class="tile is-ancestor">
<div class="tile is-7 is-parent">
<div class="tile is-child box">
{% include 'snippets/about.html' %}
</div>
</div>
<div class="tile is-5 is-parent">
<div class="tile is-child box has-background-primary-light content">
{% if site.allow_registration %}
<h2 class="title">Join {{ site.name }}</h2>
<form name="register" method="post" action="/register">
{% include 'snippets/register_form.html' %}
</form>
{% else %}
<h2 class="title">This instance is closed</h2>
<p>{{ site.registration_closed_text | safe}}</p>
{% endif %}
</div>
</div>
</section>
{% endif %}
<div class="block is-hidden-tablet"> <div class="block is-hidden-tablet">
<h2 class="title has-text-centered">Recent Books</h2> <h2 class="title has-text-centered">{% trans "Recent Books" %}</h2>
</div> </div>
<section class="tile is-ancestor"> <section class="tile is-ancestor">

View file

@ -1,21 +0,0 @@
<section class="level is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="title has-text-weight-normal"><span class="icon icon-graphic-paperplane"></span></p>
<p class="heading">Decentralized</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title has-text-weight-normal"><span class="icon icon-graphic-heart"></span></p>
<p class="heading">Friendly</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title has-text-weight-normal"><span class="icon icon-graphic-banknote"></span></p>
<p class="heading">Anti-Corporate</p>
</div>
</div>
</section>

View file

@ -0,0 +1,67 @@
{% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %}
{% block content %}
<header class="block has-text-centered">
<h1 class="title">{{ site.name }}</h1>
<h2 class="subtitle">{{ site.instance_tagline }}</h2>
</header>
<section class="level is-mobile">
<div class="level-item has-text-centered">
<div>
<p class="title has-text-weight-normal"><span class="icon icon-graphic-paperplane"></span></p>
<p class="heading">{% trans "Decentralized" %}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title has-text-weight-normal"><span class="icon icon-graphic-heart"></span></p>
<p class="heading">{% trans "Friendly" %}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="title has-text-weight-normal"><span class="icon icon-graphic-banknote"></span></p>
<p class="heading">{% trans "Anti-Corporate" %}</p>
</div>
</div>
</section>
<section class="tile is-ancestor">
<div class="tile is-7 is-parent">
<div class="tile is-child box">
{% include 'snippets/about.html' %}
</div>
</div>
<div class="tile is-5 is-parent">
{% if not request.user.is_authenticated %}
<div class="tile is-child box has-background-primary-light content">
{% if site.allow_registration %}
<h2 class="title">{% blocktrans with name=site.name %}Join {{ name }}{% endblocktrans %}</h2>
<form name="register" method="post" action="/register">
{% include 'snippets/register_form.html' %}
</form>
{% else %}
<h2 class="title">{% trans "This instance is closed" %}</h2>
<p>{{ site.registration_closed_text | safe}}</p>
{% endif %}
</div>
{% else %}
<div class="tile is-child box has-background-white-bis">
<h2 class="title is-4">{% trans "Your Account" %}</h2>
{% include 'user/user_preview.html' with user=request.user %}
<div class="box content">
{% if request.user.summary %}
{{ request.user.summary | to_markdown | safe }}
{% endif %}
</div>
</div>
{% endif %}
</div>
</section>
{% block panel %}{% endblock %}
{% endblock %}

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load humanize %} {% load humanize %}
{% block content %} {% block content %}
<header class="block"> <header class="block">
@ -6,9 +7,9 @@
Edit "{{ author.name }}" Edit "{{ author.name }}"
</h1> </h1>
<div> <div>
<p>Added: {{ author.created_date | naturaltime }}</p> <p>{% trans "Added:" %} {{ author.created_date | naturaltime }}</p>
<p>Updated: {{ author.updated_date | naturaltime }}</p> <p>{% trans "Updated:" %} {{ author.updated_date | naturaltime }}</p>
<p>Last edited by: <a href="{{ author.last_edited_by.remote_id }}">{{ author.last_edited_by.display_name }}</a></p> <p>{% trans "Last edited by:" %} <a href="{{ author.last_edited_by.remote_id }}">{{ author.last_edited_by.display_name }}</a></p>
</div> </div>
</header> </header>
@ -24,45 +25,45 @@
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<h2 class="title is-4">Metadata</h2> <h2 class="title is-4">{% trans "Metadata" %}</h2>
<p><label class="label" for="id_name">Name:</label> {{ form.name }}</p> <p><label class="label" for="id_name">{% trans "Name:" %}</label> {{ form.name }}</p>
{% for error in form.name.errors %} {% for error in form.name.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p><label class="label" for="id_bio">Bio:</label> {{ form.bio }}</p> <p><label class="label" for="id_bio">{% trans "Bio:" %}</label> {{ form.bio }}</p>
{% for error in form.bio.errors %} {% for error in form.bio.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p><label class="label" for="id_wikipedia_link">Wikipedia link:</label> {{ form.wikipedia_link }}</p> <p><label class="label" for="id_wikipedia_link">{% trans "Wikipedia link:" %}</label> {{ form.wikipedia_link }}</p>
{% for error in form.wikipedia_link.errors %} {% for error in form.wikipedia_link.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p><label class="label" for="id_born">Birth date:</label> {{ form.born }}</p> <p><label class="label" for="id_born">{% trans "Birth date:" %}</label> {{ form.born }}</p>
{% for error in form.born.errors %} {% for error in form.born.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p><label class="label" for="id_died">Death date:</label> {{ form.died }}</p> <p><label class="label" for="id_died">{% trans "Death date:" %}</label> {{ form.died }}</p>
{% for error in form.died.errors %} {% for error in form.died.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
</div> </div>
<div class="column"> <div class="column">
<h2 class="title is-4">Author Identifiers</h2> <h2 class="title is-4">{% trans "Author Identifiers" %}</h2>
<p><label class="label" for="id_openlibrary_key">Openlibrary key:</label> {{ form.openlibrary_key }}</p> <p><label class="label" for="id_openlibrary_key">{% trans "Openlibrary key:" %}</label> {{ form.openlibrary_key }}</p>
{% for error in form.openlibrary_key.errors %} {% for error in form.openlibrary_key.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p><label class="label" for="id_librarything_key">Librarything key:</label> {{ form.librarything_key }}</p> <p><label class="label" for="id_librarything_key">{% trans "Librarything key:" %}</label> {{ form.librarything_key }}</p>
{% for error in form.librarything_key.errors %} {% for error in form.librarything_key.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p><label class="label" for="id_goodreads_key">Goodreads key:</label> {{ form.goodreads_key }}</p> <p><label class="label" for="id_goodreads_key">{% trans "Goodreads key:" %}</label> {{ form.goodreads_key }}</p>
{% for error in form.goodreads_key.errors %} {% for error in form.goodreads_key.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
@ -71,10 +72,9 @@
</div> </div>
<div class="block"> <div class="block">
<button class="button is-primary" type="submit">Save</button> <button class="button is-primary" type="submit">{% trans "Save" %}</button>
<a class="button" href="/author/{{ author.id }}">Cancel</a> <a class="button" href="/author/{{ author.id }}">{% trans "Cancel" %}</a>
</div> </div>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load humanize %} {% load humanize %}
{% block content %} {% block content %}
<header class="block"> <header class="block">
@ -6,9 +7,9 @@
Edit "{{ book.title }}" Edit "{{ book.title }}"
</h1> </h1>
<div> <div>
<p>Added: {{ book.created_date | naturaltime }}</p> <p>{% trans "Added:" %} {{ book.created_date | naturaltime }}</p>
<p>Updated: {{ book.updated_date | naturaltime }}</p> <p>{% trans "Updated:" %} {{ book.updated_date | naturaltime }}</p>
<p>Last edited by: <a href="{{ book.last_edited_by.remote_id }}">{{ book.last_edited_by.display_name }}</a></p> <p>{% trans "Last edited by:" %} <a href="{{ book.last_edited_by.remote_id }}">{{ book.last_edited_by.display_name }}</a></p>
</div> </div>
</header> </header>
@ -23,32 +24,32 @@
<input type="hidden" name="last_edited_by" value="{{ request.user.id }}"> <input type="hidden" name="last_edited_by" value="{{ request.user.id }}">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<h2 class="title is-4">Metadata</h2> <h2 class="title is-4">{% trans "Metadata" %}</h2>
<p class="fields is-grouped"><label class="label" for="id_title">Title:</label> {{ form.title }} </p> <p class="fields is-grouped"><label class="label" for="id_title">{% trans "Title:" %}</label> {{ form.title }} </p>
{% for error in form.title.errors %} {% for error in form.title.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_subtitle">Subtitle:</label> {{ form.subtitle }} </p> <p class="fields is-grouped"><label class="label" for="id_subtitle">{% trans "Subtitle:" %}</label> {{ form.subtitle }} </p>
{% for error in form.subtitle.errors %} {% for error in form.subtitle.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_description">Description:</label> {{ form.description }} </p> <p class="fields is-grouped"><label class="label" for="id_description">{% trans "Description:" %}</label> {{ form.description }} </p>
{% for error in form.description.errors %} {% for error in form.description.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_series">Series:</label> {{ form.series }} </p> <p class="fields is-grouped"><label class="label" for="id_series">{% trans "Series:" %}</label> {{ form.series }} </p>
{% for error in form.series.errors %} {% for error in form.series.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_series_number">Series number:</label> {{ form.series_number }} </p> <p class="fields is-grouped"><label class="label" for="id_series_number">{% trans "Series number:" %}</label> {{ form.series_number }} </p>
{% for error in form.series_number.errors %} {% for error in form.series_number.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_first_published_date">First published date:</label> {{ form.first_published_date }} </p> <p class="fields is-grouped"><label class="label" for="id_first_published_date">{% trans "First published date:" %}</label> {{ form.first_published_date }} </p>
{% for error in form.first_published_date.errors %} {% for error in form.first_published_date.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_published_date">Published date:</label> {{ form.published_date }} </p> <p class="fields is-grouped"><label class="label" for="id_published_date">{% trans "Published date:" %}</label> {{ form.published_date }} </p>
{% for error in form.published_date.errors %} {% for error in form.published_date.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
@ -61,7 +62,7 @@
</div> </div>
<div class="column is-narrow"> <div class="column is-narrow">
<div class="block"> <div class="block">
<h2 class="title is-4">Cover</h2> <h2 class="title is-4">{% trans "Cover" %}</h2>
<p>{{ form.cover }}</p> <p>{{ form.cover }}</p>
{% for error in form.cover.errors %} {% for error in form.cover.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
@ -71,8 +72,8 @@
</div> </div>
<div class="block"> <div class="block">
<h2 class="title is-4">Physical Properties</h2> <h2 class="title is-4">{% trans "Physical Properties" %}</h2>
<p class="fields is-grouped"><label class="label" for="id_physical_format">Format:</label> {{ form.physical_format }} </p> <p class="fields is-grouped"><label class="label" for="id_physical_format">{% trans "Format:" %}</label> {{ form.physical_format }} </p>
{% for error in form.physical_format.errors %} {% for error in form.physical_format.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
@ -80,31 +81,31 @@
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_pages">Pages:</label> {{ form.pages }} </p> <p class="fields is-grouped"><label class="label" for="id_pages">{% trans "Pages:" %}</label> {{ form.pages }} </p>
{% for error in form.pages.errors %} {% for error in form.pages.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
</div> </div>
<div class="block"> <div class="block">
<h2 class="title is-4">Book Identifiers</h2> <h2 class="title is-4">{% trans "Book Identifiers" %}</h2>
<p class="fields is-grouped"><label class="label" for="id_isbn_13">ISBN 13:</label> {{ form.isbn_13 }} </p> <p class="fields is-grouped"><label class="label" for="id_isbn_13">{% trans "ISBN 13:" %}</label> {{ form.isbn_13 }} </p>
{% for error in form.isbn_13.errors %} {% for error in form.isbn_13.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_isbn_10">ISBN 10:</label> {{ form.isbn_10 }} </p> <p class="fields is-grouped"><label class="label" for="id_isbn_10">{% trans "ISBN 10:" %}</label> {{ form.isbn_10 }} </p>
{% for error in form.isbn_10.errors %} {% for error in form.isbn_10.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_openlibrary_key">Openlibrary key:</label> {{ form.openlibrary_key }} </p> <p class="fields is-grouped"><label class="label" for="id_openlibrary_key">{% trans "Openlibrary key:" %}</label> {{ form.openlibrary_key }} </p>
{% for error in form.openlibrary_key.errors %} {% for error in form.openlibrary_key.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_librarything_key">OCLC Number:</label> {{ form.oclc_number }} </p> <p class="fields is-grouped"><label class="label" for="id_librarything_key">{% trans "OCLC Number:" %}</label> {{ form.oclc_number }} </p>
{% for error in form.oclc_number.errors %} {% for error in form.oclc_number.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
<p class="fields is-grouped"><label class="label" for="id_asin">ASIN:</label> {{ form.asin }} </p> <p class="fields is-grouped"><label class="label" for="id_asin">{% trans "ASIN:" %}</label> {{ form.asin }} </p>
{% for error in form.ASIN.errors %} {% for error in form.ASIN.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
@ -113,8 +114,8 @@
</div> </div>
<div class="block"> <div class="block">
<button class="button is-primary" type="submit">Save</button> <button class="button is-primary" type="submit">{% trans "Save" %}</button>
<a class="button" href="/book/{{ book.id }}">Cancel</a> <a class="button" href="/book/{{ book.id }}">{% trans "Cancel" %}</a>
</div> </div>
</form> </form>

View file

@ -1,8 +1,9 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Editions of <a href="/book/{{ work.id }}">"{{ work.title }}"</a></h1> <h1 class="title">{% blocktrans with path=work.local_path work_title=work.title %}Editions of <a href="{{ work_path }}">"{{ work_title }}"</a>{% endblocktrans %}</h1>
{% include 'snippets/book_tiles.html' with books=editions %} {% include 'snippets/book_tiles.html' with books=editions %}
</div> </div>

View file

@ -1,9 +1,10 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Server Error</h1> <h1 class="title">{% trans "Server Error" %}</h1>
<p>Something went wrong! Sorry about that.</p> <p>{% trans "Something went wrong! Sorry about that." %}</p>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,9 +1,10 @@
{% extends 'feed/feed_layout.html' %} {% extends 'feed/feed_layout.html' %}
{% load i18n %}
{% block panel %} {% block panel %}
<header class="block"> <header class="block">
<h1 class="title">Direct Messages{% if partner %} with {% include 'snippets/username.html' with user=partner %}{% endif %}</h1> <h1 class="title">Direct Messages{% if partner %} with {% include 'snippets/username.html' with user=partner %}{% endif %}</h1>
{% if partner %}<p class="subtitle"><a href="/direct-messages"><span class="icon icon-arrow-left" aria-hidden="true"></span> All messages</a></p>{% endif %} {% if partner %}<p class="subtitle"><a href="/direct-messages"><span class="icon icon-arrow-left" aria-hidden="true"></span> {% trans "All messages" %}</a></p>{% endif %}
</header> </header>
<div class="box"> <div class="box">
@ -12,7 +13,7 @@
<section class="block"> <section class="block">
{% if not activities %} {% if not activities %}
<p>You have no messages right now.</p> <p>{% trans "You have no messages right now." %}</p>
{% endif %} {% endif %}
{% for activity in activities %} {% for activity in activities %}
<div class="block"> <div class="block">

View file

@ -1,18 +1,19 @@
{% extends 'feed/feed_layout.html' %} {% extends 'feed/feed_layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block panel %} {% block panel %}
<h1 class="title">{{ tab | title }} Timeline</h1> <h1 class="title">{% blocktrans with tab_title=tab|title %}{{ tab_title }} Timeline{% endblocktrans %}</h1>
<div class="tabs"> <div class="tabs">
<ul> <ul>
<li class="{% if tab == 'home' %}is-active{% endif %}"> <li class="{% if tab == 'home' %}is-active{% endif %}">
<a href="/#feed">Home</a> <a href="/#feed">{% trans "Home" %}</a>
</li> </li>
<li class="{% if tab == 'local' %}is-active{% endif %}"> <li class="{% if tab == 'local' %}is-active{% endif %}">
<a href="/local#feed">Local</a> <a href="/local#feed">{% trans "Local" %}</a>
</li> </li>
<li class="{% if tab == 'federated' %}is-active{% endif %}"> <li class="{% if tab == 'federated' %}is-active{% endif %}">
<a href="/federated#feed">Federated</a> <a href="/federated#feed">{% trans "Federated" %}</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -20,7 +21,7 @@
{# announcements and system messages #} {# announcements and system messages #}
{% if not goal and tab == 'home' %} {% if not goal and tab == 'home' %}
{% now 'Y' as year %} {% now 'Y' as year %}
<section class="block hidden" aria-title="Announcements" data-hide="hide-{{ year }}-reading-goal"> <section class="block hidden" aria-title="{% trans 'Announcements' %}" data-hide="hide-{{ year }}-reading-goal">
{% include 'snippets/goal_card.html' with year=year %} {% include 'snippets/goal_card.html' with year=year %}
<hr> <hr>
</section> </section>
@ -28,7 +29,7 @@
{# activity feed #} {# activity feed #}
{% if not activities %} {% if not activities %}
<p>There aren't any activities right now! Try following a user to get started</p> <p>{% trans "There aren't any activities right now! Try following a user to get started" %}</p>
{% endif %} {% endif %}
{% for activity in activities %} {% for activity in activities %}
<div class="block"> <div class="block">

View file

@ -1,13 +1,14 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block content %} {% block content %}
<div class="columns"> <div class="columns">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<div class="column is-one-third"> <div class="column is-one-third">
<h2 class="title is-5">Your books</h2> <h2 class="title is-5">{% trans "Your books" %}</h2>
{% if not suggested_books %} {% if not suggested_books %}
<p>There are no books here right now! Try searching for a book to get started</p> <p>{% trans "There are no books here right now! Try searching for a book to get started" %}</p>
{% else %} {% else %}
<seven-minute-tabs> <seven-minute-tabs>
<div class="tabs is-small"> <div class="tabs is-small">
@ -66,7 +67,7 @@
{% if goal %} {% if goal %}
<section class="section"> <section class="section">
<div class="block"> <div class="block">
<h3 class="title is-4">{{ goal.year }} Reading Goal</h3> <h3 class="title is-4">{% blocktrans with yar=goal.year %}{{ year }} Reading Goal{% endblocktrans %}</h3>
{% include 'snippets/goal_progress.html' with goal=goal %} {% include 'snippets/goal_progress.html' with goal=goal %}
</div> </div>
</section> </section>

View file

@ -1,9 +1,10 @@
{% extends 'feed/feed_layout.html' %} {% extends 'feed/feed_layout.html' %}
{% load i18n %}
{% block panel %} {% block panel %}
<header class="block"> <header class="block">
<a href="/#feed" class="button" data-back> <a href="/#feed" class="button" data-back>
<span class="icon icon-arrow-left" aira-hidden="true"></span> <span class="icon icon-arrow-left" aira-hidden="true"></span>
<span>Back</span> <span>{% trans "Back" %}</span>
</a> </a>
</header> </header>

View file

@ -1,9 +1,10 @@
{% extends 'user/user_layout.html' %} {% extends 'user/user_layout.html' %}
{% load i18n %}
{% block header %} {% block header %}
<div class="columns is-mobile"> <div class="columns is-mobile">
<div class="column"> <div class="column">
<h1 class="title">{{ year }} Reading Progress</h1> <h1 class="title">{% blocktrans %}{{ year }} Reading Progress{% endblocktrans %}</h1>
</div> </div>
{% if is_self and goal %} {% if is_self and goal %}
<div class="column is-narrow"> <div class="column is-narrow">
@ -25,7 +26,7 @@
</h2> </h2>
</header> </header>
<section class="card-content content"> <section class="card-content content">
<p>Set a goal for how many books you'll finish reading in {{ year }}, and track your progress throughout the year.</p> <p>{% blocktrans %}Set a goal for how many books you'll finish reading in {{ year }}, and track your progress throughout the year.{% endblocktrans %}</p>
{% include 'snippets/goal_form.html' with goal=goal year=year %} {% include 'snippets/goal_form.html' with goal=goal year=year %}
</section> </section>
@ -34,7 +35,7 @@
{% endif %} {% endif %}
{% if not goal and user != request.user %} {% if not goal and user != request.user %}
<p>{{ user.display_name }} hasn't set a reading goal for {{ year }}.</p> <p>{% blocktrans with name=user.display_name %}{{ name }} hasn't set a reading goal for {{ year }}.{% endblocktrans %}</p>
{% endif %} {% endif %}
{% if goal %} {% if goal %}

View file

@ -1,32 +1,48 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load humanize %} {% load humanize %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Import Books from GoodReads</h1> <h1 class="title">{% trans "Import Books" %}</h1>
<form name="import" action="/import" method="post" enctype="multipart/form-data"> <form name="import" action="/import" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<label class="label" for="source">
<p>{% trans "Data source" %}</p>
<div class="select {{ class }}">
<select name="source" id="source">
<option value="GoodReads" {% if current == 'GoodReads' %}selected{% endif %}>
GoodReads
</option>
<option value="LibraryThing" {% if current == 'LibraryThing' %}selected{% endif %}>
LibraryThing
</option>
</select>
</div>
</label>
<div class="field"> <div class="field">
{{ import_form.as_p }} {{ import_form.as_p }}
</div> </div>
<div class="field"> <div class="field">
<label class="label"> <label class="label">
<input type="checkbox" name="include_reviews" checked> Include reviews <input type="checkbox" name="include_reviews" checked> {% trans "Include reviews" %}
</label> </label>
</div> </div>
<div class="field"> <div class="field">
<label class="label"> <label class="label">
<p>Privacy setting for imported reviews:</p> <p>{% trans "Privacy setting for imported reviews:" %}</p>
{% include 'snippets/privacy_select.html' with no_label=True %} {% include 'snippets/privacy_select.html' with no_label=True %}
</label> </label>
</div> </div>
<button class="button is-primary" type="submit">Import</button> <button class="button is-primary" type="submit">{% trans "Import" %}</button>
</form> </form>
</div> </div>
<div class="content block"> <div class="content block">
<h2 class="title">Recent Imports</h2> <h2 class="title">{% trans "Recent Imports" %}</h2>
{% if not jobs %} {% if not jobs %}
<p>No recent imports</p> <p>{% trans "No recent imports" %}</p>
{% endif %} {% endif %}
<ul> <ul>
{% for job in jobs %} {% for job in jobs %}

View file

@ -1,34 +1,35 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load humanize %} {% load humanize %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Import Status</h1> <h1 class="title">{% trans "Import Status" %}</h1>
<p> <p>
Import started: {{ job.created_date | naturaltime }} {% trans "Import started: " %}{{ job.created_date | naturaltime }}
</p> </p>
{% if job.complete %} {% if job.complete %}
<p> <p>
Import completed: {{ task.date_done | naturaltime }} {% trans "Import completed: " %}{{ task.date_done | naturaltime }}
</p> </p>
{% elif task.failed %} {% elif task.failed %}
<div class="notification is-danger">TASK FAILED</div> <div class="notification is-danger">{% trans "TASK FAILED" %}</div>
{% endif %} {% endif %}
</div> </div>
<div class="block"> <div class="block">
{% if not job.complete %} {% if not job.complete %}
Import still in progress. {% trans "Import still in progress." %}
<p> <p>
(Hit reload to update!) {% trans "(Hit reload to update!)" %}
</p> </p>
{% endif %} {% endif %}
</div> </div>
{% if failed_items %} {% if failed_items %}
<div class="block"> <div class="block">
<h2 class="title is-4">Failed to load</h2> <h2 class="title is-4">{% trans "Failed to load" %}</h2>
{% if not job.retry %} {% if not job.retry %}
<form name="retry" action="/import/{{ job.id }}" method="post"> <form name="retry" action="/import/{{ job.id }}" method="post">
{% csrf_token %} {% csrf_token %}
@ -52,10 +53,10 @@
<div class="block pt-1 select-all"> <div class="block pt-1 select-all">
<label class="label"> <label class="label">
<input type="checkbox" class="checkbox"> <input type="checkbox" class="checkbox">
Select all {% trans "Select all" %}
</label> </label>
</div> </div>
<button class="button" type="submit">Retry items</button> <button class="button" type="submit">{% trans "Retry items" %}</button>
{% else %} {% else %}
<ul> <ul>
{% for item in failed_items %} {% for item in failed_items %}
@ -77,17 +78,17 @@
{% endif %} {% endif %}
<div class="block"> <div class="block">
<h2 class="title is-4">Successfully imported</h2> <h2 class="title is-4">{% trans "Successfully imported" %}</h2>
<table class="table"> <table class="table">
<tr> <tr>
<th> <th>
Book {% trans "Book" %}
</th> </th>
<th> <th>
Title {% trans "Title" %}
</th> </th>
<th> <th>
Author {% trans "Author" %}
</th> </th>
<th> <th>
</th> </th>
@ -110,7 +111,7 @@
<td> <td>
{% if item.book %} {% if item.book %}
<span class="icon icon-check"> <span class="icon icon-check">
<span class="is-sr-only">Imported</span> <span class="is-sr-only">{% trans "Imported" %}</span>
</span> </span>
{% endif %} {% endif %}
</td> </td>

View file

@ -1,11 +1,12 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<div class="block"> <div class="block">
{% if valid %} {% if valid %}
<h1 class="title">Create an Account</h1> <h1 class="title">{% trans "Create an Account" %}</h1>
<div> <div>
<form name="register" method="post" action="/register"> <form name="register" method="post" action="/register">
<input type=hidden name="invite_code" value="{{ invite.code }}"> <input type=hidden name="invite_code" value="{{ invite.code }}">
@ -14,8 +15,8 @@
</div> </div>
{% else %} {% else %}
<div class="content"> <div class="content">
<h1 class="title">Permission Denied</h1> <h1 class="title">{% trans "Permission Denied" %}</h1>
<p>Sorry! This invite code is no longer valid.</p> <p>{% trans "Sorry! This invite code is no longer valid." %}</p>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View file

@ -1,4 +1,5 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -33,8 +34,8 @@
</div> </div>
<div class="control"> <div class="control">
<button class="button" type="submit"> <button class="button" type="submit">
<span class="icon icon-search" title="Search"> <span class="icon icon-search" title="{% trans 'Search' %}">
<span class="is-sr-only">search</span> <span class="is-sr-only">{% trans "search" %}</span>
</span> </span>
</button> </button>
</div> </div>
@ -43,8 +44,8 @@
<div role="button" tabindex="0" class="navbar-burger pulldown-menu" data-controls="main-nav" aria-expanded="false"> <div role="button" tabindex="0" class="navbar-burger pulldown-menu" data-controls="main-nav" aria-expanded="false">
<div class="navbar-item mt-3"> <div class="navbar-item mt-3">
<div class="icon icon-dots-three-vertical" title="Main navigation menu"> <div class="icon icon-dots-three-vertical" title="{% trans 'Main navigation menu' %}">
<span class="is-sr-only">Main navigation menu</span> <span class="is-sr-only">{% trans "Main navigation menu" %}</span>
</div> </div>
</div> </div>
</div> </div>
@ -54,13 +55,13 @@
<div class="navbar-start"> <div class="navbar-start">
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<a href="{% url 'user-shelves' request.user.localname %}" class="navbar-item"> <a href="{% url 'user-shelves' request.user.localname %}" class="navbar-item">
Your shelves {% trans "Your shelves" %}
</a> </a>
<a href="/#feed" class="navbar-item"> <a href="/#feed" class="navbar-item">
Feed {% trans "Feed" %}
</a> </a>
<a href="{% url 'lists' %}" class="navbar-item"> <a href="{% url 'lists' %}" class="navbar-item">
Lists {% trans "Lists" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
@ -121,8 +122,8 @@
<div class="navbar-item"> <div class="navbar-item">
<a href="/notifications" class="tags has-addons"> <a href="/notifications" class="tags has-addons">
<span class="tag is-medium"> <span class="tag is-medium">
<span class="icon icon-bell" title="Notifications"> <span class="icon icon-bell" title="{% trans 'Notifications' %}">
<span class="is-sr-only">Notifications</span> <span class="is-sr-only">{% trans "Notifications" %}</span>
</span> </span>
</span> </span>
<span class="{% if not request.user|notification_count %}hidden {% endif %}tag is-danger is-medium" data-poll="notifications"> <span class="{% if not request.user|notification_count %}hidden {% endif %}tag is-danger is-medium" data-poll="notifications">
@ -139,16 +140,16 @@
{% csrf_token %} {% csrf_token %}
<div class="columns is-variable is-1"> <div class="columns is-variable is-1">
<div class="column"> <div class="column">
<label class="is-sr-only" for="id_localname">Username:</label> <label class="is-sr-only" for="id_localname">{% trans "Username:" %}</label>
<input type="text" name="localname" maxlength="150" class="input" required="" id="id_localname" placeholder="username"> <input type="text" name="localname" maxlength="150" class="input" required="" id="id_localname" placeholder="username">
</div> </div>
<div class="column"> <div class="column">
<label class="is-sr-only" for="id_password">Username:</label> <label class="is-sr-only" for="id_password">{% trans "Username:" %}</label>
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password" placeholder="password"> <input type="password" name="password" maxlength="128" class="input" required="" id="id_password" placeholder="password">
<p class="help"><a href="/password-reset">Forgot your password?</a></p> <p class="help"><a href="/password-reset">Forgot your password?</a></p>
</div> </div>
<div class="column is-narrow"> <div class="column is-narrow">
<button class="button is-primary" type="submit">Log in</button> <button class="button is-primary" type="submit">{% trans "Log in" %}</button>
</div> </div>
</div> </div>
</form> </form>
@ -179,11 +180,11 @@
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<p> <p>
<a href="/about">About this server</a> <a href="/about">{% trans "About this server" %}</a>
</p> </p>
{% if site.admin_email %} {% if site.admin_email %}
<p> <p>
<a href="mailto:{{ site.admin_email }}">Contact site admin</a> <a href="mailto:{{ site.admin_email }}">{% trans "Contact site admin" %}</a>
</p> </p>
{% endif %} {% endif %}
</div> </div>

View file

@ -1,7 +1,8 @@
{% extends 'components/inline_form.html' %} {% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %} {% block header %}
Create List {% trans "Create List" %}
{% endblock %} {% endblock %}
{% block form %} {% block form %}

View file

@ -1,17 +1,18 @@
{% extends 'lists/list_layout.html' %} {% extends 'lists/list_layout.html' %}
{% load i18n %}
{% block panel %} {% block panel %}
<section class="content block"> <section class="content block">
<h2>Pending Books</h2> <h2>{% trans "Pending Books" %}</h2>
<p><a href="{% url 'list' list.id %}">Go to list</a></p> <p><a href="{% url 'list' list.id %}">{% trans "Go to list" %}</a></p>
{% if not pending.exists %} {% if not pending.exists %}
<p>You're all set!</p> <p>{% trans "You're all set!" %}</p>
{% else %} {% else %}
<table class="table is-striped"> <table class="table is-striped">
<tr> <tr>
<th></th> <th></th>
<th>Book</th> <th>{% trans "Book" %}</th>
<th>Suggested by</th> <th>{% trans "Suggested by" %}</th>
<th></th> <th></th>
</tr> </tr>
{% for item in pending %} {% for item in pending %}
@ -31,13 +32,13 @@
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="item" value="{{ item.id }}"> <input type="hidden" name="item" value="{{ item.id }}">
<input type="hidden" name="approved" value="true"> <input type="hidden" name="approved" value="true">
<button class="button">Approve</button> <button class="button">{% trans "Approve" %}</button>
</form> </form>
<form class="control" method="POST" action="{% url 'list-curate' list.id %}"> <form class="control" method="POST" action="{% url 'list-curate' list.id %}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="item" value="{{ item.id }}"> <input type="hidden" name="item" value="{{ item.id }}">
<input type="hidden" name="approved" value="false"> <input type="hidden" name="approved" value="false">
<button class="button is-danger is-light">Discard</button> <button class="button is-danger is-light">{% trans "Discard" %}</button>
</div> </div>
</form> </form>
</td> </td>

View file

@ -1,7 +1,8 @@
{% extends 'components/inline_form.html' %} {% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %} {% block header %}
Edit List {% trans "Edit List" %}
{% endblock %} {% endblock %}
{% block form %} {% block form %}

View file

@ -1,34 +1,35 @@
{% load i18n %}
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}"> <input type="hidden" name="user" value="{{ request.user.id }}">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<div class="field"> <div class="field">
<label class="label" for="id_name">Name:</label> <label class="label" for="id_name">{% trans "Name:" %}</label>
{{ list_form.name }} {{ list_form.name }}
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="id_description">Description:</label> <label class="label" for="id_description">{% trans "Description:" %}</label>
{{ list_form.description }} {{ list_form.description }}
</div> </div>
</div> </div>
<div class="column"> <div class="column">
<fieldset class="field"> <fieldset class="field">
<legend class="label">List curation:</legend> <legend class="label">{% trans "List curation:" %}</legend>
<label class="field"> <label class="field">
<input type="radio" name="curation" value="closed"{% if not list or list.curation == 'closed' %} checked{% endif %}> Closed <input type="radio" name="curation" value="closed"{% if not list or list.curation == 'closed' %} checked{% endif %}> {% trans "Closed" %}
<p class="help mb-2">Only you can add and remove books to this list</p> <p class="help mb-2">{% trans "Only you can add and remove books to this list" %}</p>
</label> </label>
<label class="field"> <label class="field">
<input type="radio" name="curation" value="curated"{% if list.curation == 'curated' %} checked{% endif %}> Curated <input type="radio" name="curation" value="curated"{% if list.curation == 'curated' %} checked{% endif %}> {% trans "Curated" %}
<p class="help mb-2">Anyone can suggest books, subject to your approval</p> <p class="help mb-2">{% trans "Anyone can suggest books, subject to your approval" %}</p>
</label> </label>
<label class="field"> <label class="field">
<input type="radio" name="curation" value="open"{% if list.curation == 'open' %} checked{% endif %}> Open <input type="radio" name="curation" value="open"{% if list.curation == 'open' %} checked{% endif %}> {% trans "Open" %}
<p class="help mb-2">Anyone can add books to this list</p> <p class="help mb-2">{% trans "Anyone can add books to this list" %}</p>
</label> </label>
</fieldset> </fieldset>
</div> </div>
@ -38,7 +39,7 @@
{% include 'snippets/privacy_select.html' with current=list.privacy %} {% include 'snippets/privacy_select.html' with current=list.privacy %}
</div> </div>
<div class="control"> <div class="control">
<button type="submit" class="button is-primary">Save</button> <button type="submit" class="button is-primary">{% trans "Save" %}</button>
</div> </div>
</div> </div>

View file

@ -1,4 +1,5 @@
{% extends 'lists/list_layout.html' %} {% extends 'lists/list_layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block panel %} {% block panel %}
@ -13,7 +14,7 @@
<div class="columns mt-3"> <div class="columns mt-3">
<section class="column is-three-quarters"> <section class="column is-three-quarters">
{% if not items.exists %} {% if not items.exists %}
<p>This list is currently empty</p> <p>{% trans "This list is currently empty" %}</p>
{% else %} {% else %}
<ol> <ol>
{% for item in items %} {% for item in items %}
@ -31,13 +32,13 @@
</div> </div>
<div class="card-footer has-background-white-bis"> <div class="card-footer has-background-white-bis">
<div class="card-footer-item"> <div class="card-footer-item">
<p>Added by {% include 'snippets/username.html' with user=item.user %}</p> <p>{% trans "Added by" %} {% include 'snippets/username.html' with user=item.user %}</p>
</div> </div>
{% if list.user == request.user or list.curation == 'open' and item.user == request.user %} {% if list.user == request.user or list.curation == 'open' and item.user == request.user %}
<form name="add-book" method="post" action="{% url 'list-remove-book' list.id %}" class="card-footer-item"> <form name="add-book" method="post" action="{% url 'list-remove-book' list.id %}" class="card-footer-item">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="item" value="{{ item.id }}"> <input type="hidden" name="item" value="{{ item.id }}">
<button type="submit" class="button is-small is-danger">Remove</button> <button type="submit" class="button is-small is-danger">{% trans "Remove" %}</button>
</form> </form>
{% endif %} {% endif %}
</div> </div>
@ -50,26 +51,29 @@
{% if request.user.is_authenticated and not list.curation == 'closed' or request.user == list.user %} {% if request.user.is_authenticated and not list.curation == 'closed' or request.user == list.user %}
<section class="column is-one-quarter content"> <section class="column is-one-quarter content">
<h2>{% if list.curation == 'open' or request.user == list.user %}Add{% else %}Suggest{% endif %} Books</h2> <h2>{% if list.curation == 'open' or request.user == list.user %}{% trans "Add Books" %}{% else %}{% trans "Suggest Books" %}{% endif %}</h2>
<form name="search" action="{% url 'list' list.id %}" method="GET" class="block"> <form name="search" action="{% url 'list' list.id %}" method="GET" class="block">
<div class="field has-addons"> <div class="field has-addons">
<div class="control"> <div class="control">
<input aria-label="Search for a book" class="input" type="text" name="q" placeholder="Search for a book" value="{{ query }}"> <input aria-label="{% trans 'Search for a book' %}" class="input" type="text" name="q" placeholder="{% trans 'Search for a book' %}" value="{{ query }}">
</div> </div>
<div class="control"> <div class="control">
<button class="button" type="submit"> <button class="button" type="submit">
<span class="icon icon-search" title="Search"> <span class="icon icon-search" title="{% trans 'Search' %}">
<span class="is-sr-only">search</span> <span class="is-sr-only">{% trans "search" %}</span>
</span> </span>
</button> </button>
</div> </div>
</div> </div>
{% if query %} {% if query %}
<p class="help"><a href="{% url 'list' list.id %}">Clear search</a></p> <p class="help"><a href="{% url 'list' list.id %}">{% trans "Clear search" %}</a></p>
{% endif %} {% endif %}
</form> </form>
{% if not suggested_books %} {% if not suggested_books %}
<p>No books found{% if query %} matching the query "{{ query }}"{% endif %}</p> {% if query %}
<p>{% blocktrans %}No books found matching the query "{{ query }}"{% endblocktrans %}</p>{% else %}
<p>{% trans "No books found" %}</p>
{% endif %}
{% endif %} {% endif %}
{% for book in suggested_books %} {% for book in suggested_books %}
{% if book %} {% if book %}
@ -82,7 +86,7 @@
<form name="add-book" method="post" action="{% url 'list-add-book' list.id %}"> <form name="add-book" method="post" action="{% url 'list-add-book' list.id %}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}"> <input type="hidden" name="book" value="{{ book.id }}">
<button type="submit" class="button is-small is-link">{% if list.curation == 'open' or request.user == list.user %}Add{% else %}Suggest{% endif %}</button> <button type="submit" class="button is-small is-link">{% if list.curation == 'open' or request.user == list.user %}{% trans "Add" %}{% else %}{% trans "Suggest" %}{% endif %}</button>
</form> </form>
</div> </div>
</div> </div>

View file

@ -1,4 +1,5 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
<div class="columns is-multiline"> <div class="columns is-multiline">
{% for list in lists %} {% for list in lists %}
<div class="column is-one-quarter"> <div class="column is-one-quarter">
@ -15,7 +16,7 @@
</div> </div>
<div class="card-content is-flex-grow-0"> <div class="card-content is-flex-grow-0">
{% if list.description %}{{ list.description | to_markdown | safe | truncatewords_html:20 }}{% endif %} {% if list.description %}{{ list.description | to_markdown | safe | truncatewords_html:20 }}{% endif %}
<p class="subtitle help">Created {% if list.curation != 'open' %} and curated{% endif %} by {% include 'snippets/username.html' with user=list.user %}</p> <p class="subtitle help">{% if list.curation != 'open' %}{% trans "Created and curated by" %}{% else %}{% trans "Created by" %}{% endif %} {% include 'snippets/username.html' with user=list.user %}</p>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,11 +1,13 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block content %} {% block content %}
<header class="columns content is-mobile"> <header class="columns content is-mobile">
<div class="column"> <div class="column">
<h1 class="title">{{ list.name }} <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span></h1> <h1 class="title">{{ list.name }} <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span></h1>
<p class="subtitle help">Created {% if list.curation != 'open' %} and curated{% endif %} by {% include 'snippets/username.html' with user=list.user %}</p> <p class="subtitle help">{% if list.curation != 'open' %}{% trans "Created and curated by" %}{% else %}{% trans "Created by" %} {% include 'snippets/username.html' with user=list.user %}</p>
{% endif %}
{% include 'snippets/trimmed_text.html' with full=list.description %} {% include 'snippets/trimmed_text.html' with full=list.description %}
</div> </div>
{% if request.user == list.user %} {% if request.user == list.user %}

View file

@ -1,13 +1,14 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<header class="block"> <header class="block">
<h1 class="title">Lists</h1> <h1 class="title">{% trans "Lists" %}</h1>
</header> </header>
{% if request.user.is_authenticated and not lists.has_previous %} {% if request.user.is_authenticated and not lists.has_previous %}
<header class="block columns is-mobile"> <header class="block columns is-mobile">
<div class="column"> <div class="column">
<h2 class="title">Your lists</h2> <h2 class="title">{% trans "Your lists" %}</h2>
</div> </div>
<div class="column is-narrow"> <div class="column is-narrow">
{% include 'snippets/toggle/open_button.html' with controls_text="create-list" icon="plus" text="Create new list" focus="create-list-header" %} {% include 'snippets/toggle/open_button.html' with controls_text="create-list" icon="plus" text="Create new list" focus="create-list-header" %}
@ -32,7 +33,7 @@
{% if lists %} {% if lists %}
<section class="block content"> <section class="block content">
<h2 class="title">Recent Lists</h2> <h2 class="title">{% trans "Recent Lists" %}</h2>
{% include 'lists/list_items.html' with lists=lists %} {% include 'lists/list_items.html' with lists=lists %}
</section> </section>
<div> <div>

View file

@ -1,23 +1,24 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<div class="box"> <div class="box">
<h1 class="title">Log in</h1> <h1 class="title">{% trans "Log in" %}</h1>
{% if login_form.non_field_errors %} {% if login_form.non_field_errors %}
<p class="notification is-danger">{{ login_form.non_field_errors }}</p> <p class="notification is-danger">{{ login_form.non_field_errors }}</p>
{% endif %} {% endif %}
<form name="login" method="post" action="/login"> <form name="login" method="post" action="/login">
{% csrf_token %} {% csrf_token %}
<div class="field"> <div class="field">
<label class="label" for="id_localname">Username:</label> <label class="label" for="id_localname">{% trans "Username:" %}</label>
<div class="control"> <div class="control">
{{ login_form.localname }} {{ login_form.localname }}
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="id_password">Password:</label> <label class="label" for="id_password">{% trans "Password:" %}</label>
<div class="control"> <div class="control">
{{ login_form.password }} {{ login_form.password }}
</div> </div>
@ -27,23 +28,23 @@
</div> </div>
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-primary" type="submit">Log in</button> <button class="button is-primary" type="submit">{% trans "Log in" %}</button>
</div> </div>
<div class="control"> <div class="control">
<small><a href="/password-reset">Forgot your password?</a></small> <small><a href="/password-reset">{% trans "Forgot your password?" %}</a></small>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="box has-background-primary-light"> <div class="box has-background-primary-light">
{% if site.allow_registration %} {% if site.allow_registration %}
<h2 class="title">Create an Account</h2> <h2 class="title">{% trans "Create an Account" %}</h2>
<form name="register" method="post" action="/register"> <form name="register" method="post" action="/register">
{% include 'snippets/register_form.html' %} {% include 'snippets/register_form.html' %}
</form> </form>
{% else %} {% else %}
<h2 class="title">This instance is closed</h2> <h2 class="title">{% trans "This instance is closed" %}</h2>
<p>Contact an administrator to get an invite</p> <p>{% trans "Contact an administrator to get an invite" %}</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -53,7 +54,7 @@
{% include 'snippets/about.html' %} {% include 'snippets/about.html' %}
<p class="block"> <p class="block">
<a href="/about/">More about this site</a> <a href="/about/">{% trans "More about this site" %}</a>
</p> </p>
</div> </div>
</div> </div>

View file

@ -1,9 +1,10 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Not Found</h1> <h1 class="title">{% trans "Not Found" %}</h1>
<p>The page your requested doesn't seem to exist!</p> <p>{% trans "The page your requested doesn't seem to exist!" %}</p>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,13 +1,14 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load humanize %} {% load humanize %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Notifications</h1> <h1 class="title">{% trans "Notifications" %}</h1>
<form name="clear" action="/notifications" method="POST"> <form name="clear" action="/notifications" method="POST">
{% csrf_token %} {% csrf_token %}
<button class="button is-danger is-light" type="submit" class="secondary">Delete notifications</button> <button class="button is-danger is-light" type="submit" class="secondary">{% trans "Delete notifications" %}</button>
</form> </form>
</div> </div>
@ -41,32 +42,41 @@
{% include 'snippets/avatar.html' with user=notification.related_user %} {% include 'snippets/avatar.html' with user=notification.related_user %}
{% include 'snippets/username.html' with user=notification.related_user %} {% include 'snippets/username.html' with user=notification.related_user %}
{% if notification.notification_type == 'FAVORITE' %} {% if notification.notification_type == 'FAVORITE' %}
{% blocktrans with preview_name=related_status|status_preview_name|safe related_path=related_status.local_path %}
favorited your favorited your
<a href="{{ related_status.local_path }}">{{ related_status | status_preview_name|safe }}</a> <a href="{{ related_path }}">{{ preview_name }}</a>
{% endblocktrans %}
{% elif notification.notification_type == 'MENTION' %} {% elif notification.notification_type == 'MENTION' %}
{% blocktrans with preview_name=related_status|status_preview_name|safe related_path=related_status.local_path %}
mentioned you in a mentioned you in a
<a href="{{ related_status.local_path }}">{{ related_status | status_preview_name|safe }}</a> <a href="{{ related_path }}">{{ preview_name }}</a>
{% endblocktrans %}
{% elif notification.notification_type == 'REPLY' %} {% elif notification.notification_type == 'REPLY' %}
<a href="{{ related_status.local_path }}">replied</a> {% blocktrans with preview_name=related_status|status_preview_name|safe related_path=related_status.local_path parent_path=related_status.reply_parent.local_path %}
<a href="{{ related_path }}">replied</a>
to your to your
<a href="{{ related_status.reply_parent.local_path }}">{{ related_status | status_preview_name|safe }}</a> <a href="{{ parent_path }}">{{ preview_name }}</a>
{% endblocktrans %}
{% elif notification.notification_type == 'FOLLOW' %} {% elif notification.notification_type == 'FOLLOW' %}
followed you {% trans "followed you" %}
{% include 'snippets/follow_button.html' with user=notification.related_user %} {% include 'snippets/follow_button.html' with user=notification.related_user %}
{% elif notification.notification_type == 'FOLLOW_REQUEST' %} {% elif notification.notification_type == 'FOLLOW_REQUEST' %}
sent you a follow request {% trans "sent you a follow request" %}
<div class="row shrink"> <div class="row shrink">
{% include 'snippets/follow_request_buttons.html' with user=notification.related_user %} {% include 'snippets/follow_request_buttons.html' with user=notification.related_user %}
</div> </div>
{% elif notification.notification_type == 'BOOST' %} {% elif notification.notification_type == 'BOOST' %}
boosted your <a href="{{ related_status.local_path }}">{{ related_status | status_preview_name|safe }}</a> {% blocktrans with preview_name=related_status|status_preview_name|safe related_path=related_status.local_path %}
boosted your <a href="{{ related_path }}">{{ preview_name }}</a>
{% endblocktrans %}
{% elif notification.notification_type == 'ADD' %} {% elif notification.notification_type == 'ADD' %}
{% if notification.related_list_item.approved %}added{% else %}suggested adding{% endif %} {% include 'snippets/book_titleby.html' with book=notification.related_list_item.book %} to your list "<a href="{{ notification.related_list_item.book_list.local_path }}{% if not notification.related_list_item.approved %}/curate{% endif %}">{{ notification.related_list_item.book_list.name }}</a>" {% if notification.related_list_item.approved %}{% trans "added" %}{% else %}{% trans "suggested adding" %}{% endif %} {% include 'snippets/book_titleby.html' with book=notification.related_list_item.book %} to your list "<a href="{{ notification.related_list_item.book_list.local_path }}{% if not notification.related_list_item.approved %}/curate{% endif %}">{{ notification.related_list_item.book_list.name }}</a>"
{% endif %} {% endif %}
{% elif notification.related_import %} {% elif notification.related_import %}
your <a href="/import/{{ notification.related_import.id }}">import</a> completed. {% blocktrans with related_id=notification.related_import.id %} your <a href="/import/{{ related_id }}">import</a> completed.{% endblocktrans %}
{% endif %} {% endif %}
</p> </p>
</div> </div>
@ -98,7 +108,7 @@
{% endfor %} {% endfor %}
{% if not notifications %} {% if not notifications %}
<p>You're all caught up!</p> <p>{% trans "You're all caught up!" %}</p>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,30 +1,31 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<div class="block"> <div class="block">
<h1 class="title">Reset Password</h1> <h1 class="title">{% trans "Reset Password" %}</h1>
{% for error in errors %} {% for error in errors %}
<p class="is-danger">{{ error }}</p> <p class="is-danger">{{ error }}</p>
{% endfor %} {% endfor %}
<form name="password-reset" method="post" action="/password-reset/{{ code }}"> <form name="password-reset" method="post" action="/password-reset/{{ code }}">
{% csrf_token %} {% csrf_token %}
<div class="field"> <div class="field">
<label class="label" for="id_password">Password:</label> <label class="label" for="id_password">{% trans "Password:" %}</label>
<div class="control"> <div class="control">
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password"> <input type="password" name="password" maxlength="128" class="input" required="" id="id_password">
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="id_confirm_password">Confirm password:</label> <label class="label" for="id_confirm_password">{% trans "Confirm password:" %}</label>
<div class="control"> <div class="control">
<input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password"> <input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password">
</div> </div>
</div> </div>
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-primary" type="submit">Confirm</button> <button class="button is-primary" type="submit">{% trans "Confirm" %}</button>
</div> </div>
</div> </div>
</form> </form>
@ -36,7 +37,6 @@
{% include 'snippets/about.html' %} {% include 'snippets/about.html' %}
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,23 +1,24 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<div class="columns is-centered"> <div class="columns is-centered">
<div class="column is-half"> <div class="column is-half">
<div class="block"> <div class="block">
<h1 class="title">Reset Password</h1> <h1 class="title">{% trans "Reset Password" %}</h1>
{% if message %}<p>{{ message }}</p>{% endif %} {% if message %}<p>{{ message }}</p>{% endif %}
<p>A link to reset your password will be sent to your email address</p> <p>{% trans "A link to reset your password will be sent to your email address" %}</p>
<form name="password-reset" method="post" action="/password-reset"> <form name="password-reset" method="post" action="/password-reset">
{% csrf_token %} {% csrf_token %}
<div class="field"> <div class="field">
<label class="label" for="id_email_register">Email address:</label> <label class="label" for="id_email_register">{% trans "Email address:" %}</label>
<div class="control"> <div class="control">
<input type="email" name="email" maxlength="254" class="input" id="id_email_register"> <input type="email" name="email" maxlength="254" class="input" id="id_email_register">
</div> </div>
</div> </div>
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-link" type="submit">Reset password</button> <button class="button is-link" type="submit">{% trans "Reset password" %}</button>
</div> </div>
</div> </div>
</form> </form>

View file

@ -1,12 +1,13 @@
{% extends 'preferences/preferences_layout.html' %} {% extends 'preferences/preferences_layout.html' %}
{% load i18n %}
{% block header %} {% block header %}
Blocked Users {% trans "Blocked Users" %}
{% endblock %} {% endblock %}
{% block panel %} {% block panel %}
{% if not request.user.blocks.exists %} {% if not request.user.blocks.exists %}
<p>No users currently blocked.</p> <p>{% trans "No users currently blocked." %}</p>
{% else %} {% else %}
<ul> <ul>
{% for user in request.user.blocks.all %} {% for user in request.user.blocks.all %}

View file

@ -1,19 +1,20 @@
{% extends 'preferences/preferences_layout.html' %} {% extends 'preferences/preferences_layout.html' %}
{% load i18n %}
{% block header %} {% block header %}
Change Password {% trans "Change Password" %}
{% endblock %} {% endblock %}
{% block panel %} {% block panel %}
<form name="edit-profile" action="/change-password/" method="post" enctype="multipart/form-data"> <form name="edit-profile" action="/change-password/" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="block"> <div class="block">
<label class="label" for="id_password">New password:</label> <label class="label" for="id_password">{% trans "New password:" %}</label>
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password"> <input type="password" name="password" maxlength="128" class="input" required="" id="id_password">
</div> </div>
<div class="block"> <div class="block">
<label class="label" for="id_confirm_password">Confirm password:</label> <label class="label" for="id_confirm_password">{% trans "Confirm password:" %}</label>
<input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password"> <input type="password" name="confirm-password" maxlength="128" class="input" required="" id="id_confirm_password">
</div> </div>
<button class="button is-primary" type="submit">Change password</button> <button class="button is-primary" type="submit">{% trans "Change password" %}</button>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -1,6 +1,7 @@
{% extends 'preferences/preferences_layout.html' %} {% extends 'preferences/preferences_layout.html' %}
{% load i18n %}
{% block header %} {% block header %}
Edit Profile {% trans "Edit Profile" %}
{% endblock %} {% endblock %}
{% block panel %} {% block panel %}
@ -10,28 +11,28 @@ Edit Profile
<form name="edit-profile" action="{% url 'prefs-profile' %}" method="post" enctype="multipart/form-data"> <form name="edit-profile" action="{% url 'prefs-profile' %}" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="block"> <div class="block">
<label class="label" for="id_avatar">Avatar:</label> <label class="label" for="id_avatar">{% trans "Avatar:" %}</label>
{{ form.avatar }} {{ form.avatar }}
{% for error in form.avatar.errors %} {% for error in form.avatar.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
</div> </div>
<div class="block"> <div class="block">
<label class="label" for="id_name">Display name:</label> <label class="label" for="id_name">{% trans "Display name:" %}</label>
{{ form.name }} {{ form.name }}
{% for error in form.name.errors %} {% for error in form.name.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
</div> </div>
<div class="block"> <div class="block">
<label class="label" for="id_summary">Summary:</label> <label class="label" for="id_summary">{% trans "Summary:" %}</label>
{{ form.summary }} {{ form.summary }}
{% for error in form.summary.errors %} {% for error in form.summary.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
{% endfor %} {% endfor %}
</div> </div>
<div class="block"> <div class="block">
<label class="label" for="id_email">Email address:</label> <label class="label" for="id_email">{% trans "Email address:" %}</label>
{{ form.email }} {{ form.email }}
{% for error in form.email.errors %} {% for error in form.email.errors %}
<p class="help is-danger">{{ error | escape }}</p> <p class="help is-danger">{{ error | escape }}</p>
@ -39,10 +40,10 @@ Edit Profile
</div> </div>
<div class="block"> <div class="block">
<label class="checkbox label" for="id_manually_approves_followers"> <label class="checkbox label" for="id_manually_approves_followers">
Manually approve followers: {% trans "Manually approve followers:" %}
{{ form.manually_approves_followers }} {{ form.manually_approves_followers }}
</label> </label>
</div> </div>
<button class="button is-primary" type="submit">Save</button> <button class="button is-primary" type="submit">{% trans "Save" %}</button>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<header class="block column is-offset-one-quarter pl-1"> <header class="block column is-offset-one-quarter pl-1">
@ -7,19 +8,19 @@
<div class="block columns"> <div class="block columns">
<nav class="menu column is-one-quarter"> <nav class="menu column is-one-quarter">
<h2 class="menu-label">Account</h2> <h2 class="menu-label">{% trans "Account" %}</h2>
<ul class="menu-list"> <ul class="menu-list">
<li> <li>
<a href="/preferences/profile"{% if '/preferences/profile' in request.path %} class="is-active" aria-selected="true"{% endif %}>Profile</a> <a href="/preferences/profile"{% if '/preferences/profile' in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Profile" %}</a>
</li> </li>
<li> <li>
<a href="/preferences/password"{% if '/preferences/password' in request.path %} class="is-active" aria-selected="true"{% endif %}>Change password</a> <a href="/preferences/password"{% if '/preferences/password' in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Change password" %}</a>
</li> </li>
</ul> </ul>
<h2 class="menu-label">Relationships</h2> <h2 class="menu-label">{% trans "Relationships" %}</h2>
<ul class="menu-list"> <ul class="menu-list">
<li> <li>
<a href="/preferences/block"{% if '/preferences/block' in request.path %} class="is-active" aria-selected="true"{% endif %}>Blocked users</a> <a href="/preferences/block"{% if '/preferences/block' in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Blocked users" %}</a>
</li> </li>
</ul> </ul>
</nav> </nav>

View file

@ -1,16 +1,17 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
{% with book_results|first as local_results %} {% with book_results|first as local_results %}
<div class="block"> <div class="block">
<h1 class="title">Search Results for "{{ query }}"</h1> <h1 class="title">{% blocktrans %}Search Results for "{{ query }}"{% endblocktrans %}</h1>
</div> </div>
<div class="block columns"> <div class="block columns">
<div class="column"> <div class="column">
<h2 class="title">Matching Books</h2> <h2 class="title">{% trans "Matching Books" %}</h2>
<section class="block"> <section class="block">
{% if not local_results.results %} {% if not local_results.results %}
<p>No books found for "{{ query }}"</p> <p>{% blocktrans %}No books found for "{{ query }}"{% endblocktrans %}</p>
{% else %} {% else %}
<ul> <ul>
{% for result in local_results.results %} {% for result in local_results.results %}
@ -26,7 +27,7 @@
{% if book_results|slice:":1" and local_results.results %} {% if book_results|slice:":1" and local_results.results %}
<div class="block"> <div class="block">
<p> <p>
Didn't find what you were looking for? {% trans "Didn't find what you were looking for?" %}
</p> </p>
{% include 'snippets/toggle/open_button.html' with text="Show results from other catalogues" small=True controls_text="more-results" %} {% include 'snippets/toggle/open_button.html' with text="Show results from other catalogues" small=True controls_text="more-results" %}
</div> </div>
@ -49,7 +50,7 @@
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="remote_id" value="{{ result.key }}"> <input type="hidden" name="remote_id" value="{{ result.key }}">
<div>{% include 'snippets/search_result_text.html' with result=result link=False %}</div> <div>{% include 'snippets/search_result_text.html' with result=result link=False %}</div>
<button type="submit" class="button is-small is-link">Import book</button> <button type="submit" class="button is-small is-link">{% trans "Import book" %}</button>
</form> </form>
</li> </li>
{% endfor %} {% endfor %}
@ -66,9 +67,9 @@
</div> </div>
<div class="column"> <div class="column">
<section class="block"> <section class="block">
<h2 class="title">Matching Users</h2> <h2 class="title">{% trans "Matching Users" %}</h2>
{% if not user_results %} {% if not user_results %}
<p>No users found for "{{ query }}"</p> <p>{% blocktrans %}No users found for "{{ query }}"{% endblocktrans %}</p>
{% endif %} {% endif %}
<ul> <ul>
{% for result in user_results %} {% for result in user_results %}
@ -81,9 +82,9 @@
</ul> </ul>
</section> </section>
<section class="block"> <section class="block">
<h2 class="title">Lists</h2> <h2 class="title">{% trans "Lists" %}</h2>
{% if not list_results %} {% if not list_results %}
<p>No lists found for "{{ query }}"</p> <p>{% blocktrans %}No lists found for "{{ query }}"{% endblocktrans %}</p>
{% endif %} {% endif %}
{% for result in list_results %} {% for result in list_results %}
<div class="block"> <div class="block">

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% block content %} {% block content %}
<header class="block column is-offset-one-quarter pl-1"> <header class="block column is-offset-one-quarter pl-1">
@ -8,30 +9,30 @@
<div class="block columns"> <div class="block columns">
<nav class="menu column is-one-quarter"> <nav class="menu column is-one-quarter">
{% if perms.bookwyrm.create_invites %} {% if perms.bookwyrm.create_invites %}
<h2 class="menu-label">Manage Users</h2> <h2 class="menu-label">{% trans "Manage Users" %}</h2>
<ul class="menu-list"> <ul class="menu-list">
<li> <li>
{% url 'settings-invites' as url %} {% url 'settings-invites' as url %}
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>Invites</a> <a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Invites" %}</a>
</li> </li>
<li> <li>
{% url 'settings-federation' as url %} {% url 'settings-federation' as url %}
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>Federated Servers</a> <a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Federated Servers" %}</a>
</li> </li>
</ul> </ul>
{% endif %} {% endif %}
{% if perms.bookwyrm.edit_instance_settings %} {% if perms.bookwyrm.edit_instance_settings %}
<h2 class="menu-label">Instance Settings</h2> <h2 class="menu-label">{% trans "Instance Settings" %}</h2>
<ul class="menu-list"> <ul class="menu-list">
<li> <li>
{% url 'settings-site' as url %} {% url 'settings-site' as url %}
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>Site Configuration</a> <a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Site Configuration" %}</a>
{% if url in request.path %} {% if url in request.path %}
<ul class="emnu-list"> <ul class="emnu-list">
<li><a href="{{ url }}#instance-info">Instance Info</a></li> <li><a href="{{ url }}#instance-info">{% trans "Instance Info" %}</a></li>
<li><a href="{{ url }}#images">Images</a></li> <li><a href="{{ url }}#images">{% trans "Images" %}</a></li>
<li><a href="{{ url }}#footer">Footer Content</a></li> <li><a href="{{ url }}#footer">{% trans "Footer Content" %}</a></li>
<li><a href="{{ url }}#registration">Registration</a></li> <li><a href="{{ url }}#registration">{% trans "Registration" %}</a></li>
</ul> </ul>
{% endif %} {% endif %}
</li> </li>

View file

@ -1,13 +1,14 @@
{% extends 'settings/admin_layout.html' %} {% extends 'settings/admin_layout.html' %}
{% block header %}Federated Servers{% endblock %} {% load i18n %}
{% block header %}{% trans "Federated Servers" %}{% endblock %}
{% block panel %} {% block panel %}
<table class="table is-striped"> <table class="table is-striped">
<tr> <tr>
<th>Server name</th> <th>{% trans "Server name" %}</th>
<th>Software</th> <th>{% trans "Software" %}</th>
<th>Status</th> <th>{% trans "Status" %}</th>
</tr> </tr>
{% for server in servers %} {% for server in servers %}
<tr> <tr>

View file

@ -1,41 +1,42 @@
{% extends 'settings/admin_layout.html' %} {% extends 'settings/admin_layout.html' %}
{% block header %}Invites{% endblock %} {% load i18n %}
{% block header %}{% trans "Invites" %}{% endblock %}
{% load humanize %} {% load humanize %}
{% block panel %} {% block panel %}
<section class="block"> <section class="block">
<h2 class="title is-4">Generate New Invite</h2> <h2 class="title is-4">{% trans "Generate New Invite" %}</h2>
<form name="invite" action="{% url 'settings-invites' %}" method="post"> <form name="invite" action="{% url 'settings-invites' %}" method="post">
{% csrf_token %} {% csrf_token %}
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<label class="label" for="id_expiry">Expiry:</label> <label class="label" for="id_expiry">{% trans "Expiry:" %}</label>
<div class="select"> <div class="select">
{{ form.expiry }} {{ form.expiry }}
</div> </div>
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_use_limit">Use limit:</label> <label class="label" for="id_use_limit">{% trans "Use limit:" %}</label>
<div class="select"> <div class="select">
{{ form.use_limit }} {{ form.use_limit }}
</div> </div>
</div> </div>
</div> </div>
<button class="button is-primary" type="submit">Create Invite</button> <button class="button is-primary" type="submit">{% trans "Create Invite" %}</button>
</form> </form>
</section> </section>
<section class="block"> <section class="block">
<table class="table is-striped"> <table class="table is-striped">
<tr> <tr>
<th>Link</th> <th>{% trans "Link" %}</th>
<th>Expires</th> <th>{% trans "Expires" %}</th>
<th>Max uses</th> <th>{% trans "Max uses" %}</th>
<th>Times used</th> <th>{% trans "Times used" %}</th>
</tr> </tr>
{% if not invites %} {% if not invites %}
<tr><td colspan="4">No active invites</td></tr> <tr><td colspan="4">{% trans "No active invites" %}</td></tr>
{% endif %} {% endif %}
{% for invite in invites %} {% for invite in invites %}
<tr> <tr>

View file

@ -1,45 +1,50 @@
{% extends 'settings/admin_layout.html' %} {% extends 'settings/admin_layout.html' %}
{% block header %}Site Configuration{% endblock %} {% load i18n %}
{% block header %}{% trans "Site Configuration" %}{% endblock %}
{% block panel %} {% block panel %}
<form action="{% url 'settings-site' %}" method="POST" class="content"> <form action="{% url 'settings-site' %}" method="POST" class="content">
{% csrf_token %} {% csrf_token %}
<section class="block" id="instance-info"> <section class="block" id="instance-info">
<h2 class="title is-4">Instance Info</h2> <h2 class="title is-4">{% trans "Instance Info" %}</h2>
<div class="control"> <div class="control">
<label class="label" for="id_name">Instance Name:</label> <label class="label" for="id_name">{% trans "Instance Name:" %}</label>
{{ site_form.name }} {{ site_form.name }}
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_instance_tagline">Tagline:</label> <label class="label" for="id_instance_tagline">{% trans "Tagline:" %}</label>
{{ site_form.instance_tagline }} {{ site_form.instance_tagline }}
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_instance_description">Instance description:</label> <label class="label" for="id_instance_description">{% trans "Instance description:" %}</label>
{{ site_form.instance_description }} {{ site_form.instance_description }}
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_code_of_conduct">Code of conduct:</label> <label class="label" for="id_code_of_conduct">{% trans "Code of conduct:" %}</label>
{{ site_form.code_of_conduct }} {{ site_form.code_of_conduct }}
</div> </div>
<div class="control">
<label class="label" for="id_privacy_policy">{% trans "Privacy Policy:" %}</label>
{{ site_form.privacy_policy }}
</div>
</section> </section>
<hr aria-hidden="true"> <hr aria-hidden="true">
<section class="block" id="images"> <section class="block" id="images">
<h2 class="title is-4">Images</h2> <h2 class="title is-4">{% trans "Images" %}</h2>
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<label class="label" for="id_logo">Logo:</label> <label class="label" for="id_logo">{% trans "Logo:" %}</label>
{{ site_form.logo }} {{ site_form.logo }}
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_logo_small">Logo small:</label> <label class="label" for="id_logo_small">{% trans "Logo small:" %}</label>
{{ site_form.logo_small }} {{ site_form.logo_small }}
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_favicon">Favicon:</label> <label class="label" for="id_favicon">{% trans "Favicon:" %}</label>
{{ site_form.favicon }} {{ site_form.favicon }}
</div> </div>
</div> </div>
@ -48,17 +53,17 @@
<hr aria-hidden="true"> <hr aria-hidden="true">
<section class="block" id="footer"> <section class="block" id="footer">
<h2 class="title is-4">Footer Content</h2> <h2 class="title is-4">{% trans "Footer Content" %}</h2>
<div class="control"> <div class="control">
<label class="label" for="id_support_link">Support link:</label> <label class="label" for="id_support_link">{% trans "Support link:" %}</label>
<input type="text" name="support_link" maxlength="255" class="input" id="id_support_link" placeholder="https://www.patreon.com/bookwyrm"{% if site.support_link %} value="{{ site.support_link }}"{% endif %}> <input type="text" name="support_link" maxlength="255" class="input" id="id_support_link" placeholder="https://www.patreon.com/bookwyrm"{% if site.support_link %} value="{{ site.support_link }}"{% endif %}>
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_support_title">Support title:</label> <label class="label" for="id_support_title">{% trans "Support title:" %}</label>
<input type="text" name="support_title" maxlength="100" class="input" id="id_support_title" placeholder="Patreon"{% if site.support_title %} value="{{ site.support_title }}"{% endif %}> <input type="text" name="support_title" maxlength="100" class="input" id="id_support_title" placeholder="Patreon"{% if site.support_title %} value="{{ site.support_title }}"{% endif %}>
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_admin_email">Admin email:</label> <label class="label" for="id_admin_email">{% trans "Admin email:" %}</label>
{{ site_form.admin_email }} {{ site_form.admin_email }}
</div> </div>
</section> </section>
@ -66,19 +71,19 @@
<hr aria-hidden="true"> <hr aria-hidden="true">
<section class="block" id="registration"> <section class="block" id="registration">
<h2 class="title is-4">Registration</h2> <h2 class="title is-4">{% trans "Registration" %}</h2>
<div class="control"> <div class="control">
<label class="label" for="id_allow_registration">Allow registration: <label class="label" for="id_allow_registration">{% trans "Allow registration:" %}
{{ site_form.allow_registration }} {{ site_form.allow_registration }}
</div> </div>
<div class="control"> <div class="control">
<label class="label" for="id_registration_closed_text">Registration closed text:</label> <label class="label" for="id_registration_closed_text">{% trans "Registration closed text:" %}</label>
{{ site_form.registration_closed_text }} {{ site_form.registration_closed_text }}
</div> </div>
</section> </section>
<footer class="block"> <footer class="block">
<button class="button is-primary" type="submit">Save Changes</button> <button class="button is-primary" type="submit">{% trans "Save Changes" %}</button>
</footer> </footer>
</form> </form>
{% endblock %} {% endblock %}

View file

@ -1,11 +1,12 @@
{% load i18n %}
{% if not user in request.user.blocks.all %} {% if not user in request.user.blocks.all %}
<form name="blocks" method="post" action="/block/{{ user.id }}"> <form name="blocks" method="post" action="/block/{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-danger is-light is-small {{ class }}" type="submit">Block</button> <button class="button is-danger is-light is-small {{ class }}" type="submit">{% trans "Block" %}</button>
</form> </form>
{% else %} {% else %}
<form name="unblocks" method="post" action="/unblock/{{ user.id }}"> <form name="unblocks" method="post" action="/unblock/{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-small {{ class }}" type="submit">Un-block</button> <button class="button is-small {{ class }}" type="submit">{% trans "Un-block" %}</button>
</form> </form>
{% endif %} {% endif %}

View file

@ -1,19 +1,20 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
{% with status.id|uuid as uuid %} {% with status.id|uuid as uuid %}
<form name="boost" action="/boost/{{ status.id }}" method="post" class="interaction boost-{{ status.id }}-{{ uuid }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}"> <form name="boost" action="/boost/{{ status.id }}" method="post" class="interaction boost-{{ status.id }}-{{ uuid }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-small" type="submit" {% if not status.boostable %}disabled{% endif %}> <button class="button is-small" type="submit" {% if not status.boostable %}disabled{% endif %}>
<span class="icon icon-boost" title="Boost status"> <span class="icon icon-boost" title="{% trans 'Boost status' %}">
<span class="is-sr-only">Boost status</span> <span class="is-sr-only">{% trans "Boost status" %}</span>
</span> </span>
</button> </button>
</form> </form>
<form name="unboost" action="/unboost/{{ status.id }}" method="post" class="interaction boost-{{ status.id }}-{{ uuid }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}"> <form name="unboost" action="/unboost/{{ status.id }}" method="post" class="interaction boost-{{ status.id }}-{{ uuid }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}-{{ uuid }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-small is-primary" type="submit"> <button class="button is-small is-primary" type="submit">
<span class="icon icon-boost" title="Un-boost status"> <span class="icon icon-boost" title="{% trans 'Un-boost status' %}">
<span class="is-sr-only">Un-boost status</span> <span class="is-sr-only">{% trans "Un-boost status" %}</span>
</span> </span>
</button> </button>
</form> </form>

View file

@ -1,4 +1,5 @@
{% load i18n %}
<div class="control{% if not parent_status.content_warning %} hidden{% endif %}" id="spoilers-{{ uuid }}"> <div class="control{% if not parent_status.content_warning %} hidden{% endif %}" id="spoilers-{{ uuid }}">
<label class="is-sr-only" for="id_content_warning-{{ uuid }}">Spoiler alert:</label> <label class="is-sr-only" for="id_content_warning-{{ uuid }}">{% trans "Spoiler alert:" %}</label>
<input type="text" name="content_warning" maxlength="255" class="input" id="id_content_warning-{{ uuid }}" placeholder="Spoilers ahead!"{% if parent_status.content_warning %} value="{{ parent_status.content_warning }}"{% endif %}> <input type="text" name="content_warning" maxlength="255" class="input" id="id_content_warning-{{ uuid }}" placeholder="{% trans 'Spoilers ahead!' %}"{% if parent_status.content_warning %} value="{{ parent_status.content_warning }}"{% endif %}>
</div> </div>

View file

@ -1,17 +1,18 @@
{% load humanize %} {% load humanize %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
<seven-minute-tabs> <seven-minute-tabs>
<div class="tabs is-boxed" role="tablist"> <div class="tabs is-boxed" role="tablist">
<ul> <ul>
<li class="is-active"> <li class="is-active">
<a href="#review-{{ book.id }}" id="tab-review-{{ book.id }}" role="tab" aria-selected="true" aria-controls="review-{{ book.id }}" data-category="tab-option-{{ book.id }}">Review</a> <a href="#review-{{ book.id }}" id="tab-review-{{ book.id }}" role="tab" aria-selected="true" aria-controls="review-{{ book.id }}" data-category="tab-option-{{ book.id }}">{% trans "Review" %}</a>
</li> </li>
<li> <li>
<a href="#comment-{{ book.id}}" id="tab-comment-{{ book.id }}" role="tab" tabindex="-1" aria-selected="false" aria-controls="comment-{{ book.id}}" data-category="tab-option-{{ book.id }}">Comment</a> <a href="#comment-{{ book.id}}" id="tab-comment-{{ book.id }}" role="tab" tabindex="-1" aria-selected="false" aria-controls="comment-{{ book.id}}" data-category="tab-option-{{ book.id }}">{% trans "Comment" %}</a>
</li> </li>
<li> <li>
<a href="#quote-{{ book.id }}" id="tab-quote-{{ book.id }}" role="tab" tabindex="-1" aria-selected="false" aria-controls="quote-{{ book.id }}" data-category="tab-option-{{ book.id }}">Quote</a> <a href="#quote-{{ book.id }}" id="tab-quote-{{ book.id }}" role="tab" tabindex="-1" aria-selected="false" aria-controls="quote-{{ book.id }}" data-category="tab-option-{{ book.id }}">{% trans "Quote" %}</a>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -1,4 +1,5 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
<form class="is-flex-grow-1" name="{{ type }}" action="/post/{% if type == 'direct' %}status{% else %}{{ type }}{% endif %}" method="post" id="tab-{{ type }}-{{ book.id }}{{ reply_parent.id }}"> <form class="is-flex-grow-1" name="{{ type }}" action="/post/{% if type == 'direct' %}status{% else %}{{ type }}{% endif %}" method="post" id="tab-{{ type }}-{{ book.id }}{{ reply_parent.id }}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}"> <input type="hidden" name="book" value="{{ book.id }}">
@ -6,7 +7,7 @@
<input type="hidden" name="reply_parent" value="{{ reply_parent.id }}"> <input type="hidden" name="reply_parent" value="{{ reply_parent.id }}">
{% if type == 'review' %} {% if type == 'review' %}
<div class="control"> <div class="control">
<label class="label" for="id_name_{{ book.id }}_{{ type }}">Title:</label> <label class="label" for="id_name_{{ book.id }}_{{ type }}">{% trans "Title:" %}</label>
<input type="text" name="name" maxlength="255" class="input" required="" id="id_name_{{ book.id }}_{{ type }}" placeholder="My {{ type }} of '{{ book.title }}'"> <input type="text" name="name" maxlength="255" class="input" required="" id="id_name_{{ book.id }}_{{ type }}" placeholder="My {{ type }} of '{{ book.title }}'">
</div> </div>
{% endif %} {% endif %}
@ -17,9 +18,9 @@
{% if type == 'review' %} {% if type == 'review' %}
<fieldset> <fieldset>
<legend class="is-sr-only">Rating</legend> <legend class="is-sr-only">{% trans "Rating" %}</legend>
<div class="field is-grouped stars form-rate-stars"> <div class="field is-grouped stars form-rate-stars">
<label class="is-sr-only" for="no-rating-{{ book.id }}">No rating</label> <label class="is-sr-only" for="no-rating-{{ book.id }}">{% trans "No rating" %}</label>
<input class="is-sr-only" type="radio" name="rating" value="" id="no-rating-{{ book.id }}" checked> <input class="is-sr-only" type="radio" name="rating" value="" id="no-rating-{{ book.id }}" checked>
{% for i in '12345'|make_list %} {% for i in '12345'|make_list %}
<input class="is-sr-only" id="book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}"> <input class="is-sr-only" id="book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}">
@ -40,7 +41,7 @@
</div> </div>
{% if type == 'quotation' %} {% if type == 'quotation' %}
<div class="control"> <div class="control">
<label class="label" for="id_content_quote-{{ book.id }}">Comment:</label> <label class="label" for="id_content_quote-{{ book.id }}">{% trans "Comment:" %}</label>
{% include 'snippets/content_warning_field.html' with parent_status=status %} {% include 'snippets/content_warning_field.html' with parent_status=status %}
<textarea name="content" class="textarea is-small" id="id_content_quote-{{ book.id }}"></textarea> <textarea name="content" class="textarea is-small" id="id_content_quote-{{ book.id }}"></textarea>
</div> </div>
@ -55,14 +56,14 @@
<div class="control"> <div class="control">
{% if type == 'direct' %} {% if type == 'direct' %}
<input type="hidden" name="privacy" value="direct"> <input type="hidden" name="privacy" value="direct">
<button type="button" class="button" aria-label="Privacy" disabled>Private</button> <button type="button" class="button" aria-label="Privacy" disabled>{% trans "Private" %}</button>
{% else %} {% else %}
{% include 'snippets/privacy_select.html' with current=reply_parent.privacy %} {% include 'snippets/privacy_select.html' with current=reply_parent.privacy %}
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="column is-narrow"> <div class="column is-narrow">
<button class="button is-link" type="submit">Post</button> <button class="button is-link" type="submit">{% trans "Post" %}</button>
</div> </div>
</div> </div>
</form> </form>

View file

@ -1,8 +1,10 @@
{% extends 'components/modal.html' %} {% extends 'components/modal.html' %}
{% block modal-title %}Delete these read dates?{% endblock %} {% load i18n %}
{% block modal-title %}{% trans "Delete these read dates?" %}{% endblock %}
{% block modal-body %} {% block modal-body %}
{% if readthrough.progress_updates|length > 0 %} {% if readthrough.progress_updates|length > 0 %}
You are deleting this readthrough and its {{ readthrough.progress_updates|length }} associated progress updates. {% blocktrans with count=readthrough.progress_updates|length %}You are deleting this readthrough and its {{ count }} associated progress updates.{% endblocktrans %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block modal-footer %} {% block modal-footer %}
@ -10,7 +12,7 @@ You are deleting this readthrough and its {{ readthrough.progress_updates|length
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="id" value="{{ readthrough.id }}"> <input type="hidden" name="id" value="{{ readthrough.id }}">
<button class="button is-danger" type="submit"> <button class="button is-danger" type="submit">
Delete {% trans "Delete" %}
</button> </button>
{% include 'snippets/toggle/toggle_button.html' with text="Cancel" controls_text="delete-readthrough" controls_uid=readthrough.id %} {% include 'snippets/toggle/toggle_button.html' with text="Cancel" controls_text="delete-readthrough" controls_uid=readthrough.id %}
</form> </form>

View file

@ -1,18 +1,19 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
{% with status.id|uuid as uuid %} {% with status.id|uuid as uuid %}
<form name="favorite" action="/favorite/{{ status.id }}" method="POST" class="interaction fav-{{ status.id }}-{{ uuid }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}"> <form name="favorite" action="/favorite/{{ status.id }}" method="POST" class="interaction fav-{{ status.id }}-{{ uuid }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-small" type="submit"> <button class="button is-small" type="submit">
<span class="icon icon-heart" title="Like status"> <span class="icon icon-heart" title="{% trans 'Like status' %}">
<span class="is-sr-only">Like status</span> <span class="is-sr-only">{% trans "Like status" %}</span>
</span> </span>
</button> </button>
</form> </form>
<form name="unfavorite" action="/unfavorite/{{ status.id }}" method="POST" class="interaction fav-{{ status.id }}-{{ uuid }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}"> <form name="unfavorite" action="/unfavorite/{{ status.id }}" method="POST" class="interaction fav-{{ status.id }}-{{ uuid }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}-{{ uuid }}">
{% csrf_token %} {% csrf_token %}
<button class="button is-primary is-small" type="submit"> <button class="button is-primary is-small" type="submit">
<span class="icon icon-heart" title="Un-like status"> <span class="icon icon-heart" title="{% trans 'Un-like status' %}">
<span class="is-sr-only">Un-like status</span> <span class="is-sr-only">{% trans "Un-like status" %}</span>
</span> </span>
</button> </button>
</form> </form>

View file

@ -1,8 +1,9 @@
{% load i18n %}
{% if request.user == user or not request.user.is_authenticated %} {% if request.user == user or not request.user.is_authenticated %}
{% elif request.user in user.follower_requests.all %} {% elif request.user in user.follower_requests.all %}
<div> <div>
Follow request already sent. {% trans "Follow request already sent." %}
</div> </div>
{% elif user in request.user.blocks.all %} {% elif user in request.user.blocks.all %}
@ -15,15 +16,15 @@ Follow request already sent.
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ user.username }}"> <input type="hidden" name="user" value="{{ user.username }}">
{% if user.manually_approves_followers %} {% if user.manually_approves_followers %}
<button class="button is-small is-link" type="submit">Send follow request</button> <button class="button is-small is-link" type="submit">{% trans "Send follow request" %}</button>
{% else %} {% else %}
<button class="button is-small is-link" type="submit">Follow</button> <button class="button is-small is-link" type="submit">{% trans "Follow" %}</button>
{% endif %} {% endif %}
</form> </form>
<form action="/unfollow/" method="POST" class="interaction follow-{{ user.id }} {% if not request.user in user.followers.all %}hidden{%endif %}" data-id="follow-{{ user.id }}"> <form action="/unfollow/" method="POST" class="interaction follow-{{ user.id }} {% if not request.user in user.followers.all %}hidden{%endif %}" data-id="follow-{{ user.id }}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ user.username }}"> <input type="hidden" name="user" value="{{ user.username }}">
<button class="button is-small is-danger is-light" type="submit">Unfollow</button> <button class="button is-small is-danger is-light" type="submit">{% trans "Unfollow" %}</button>
</form> </form>
</div> </div>
<div class="control"> <div class="control">

View file

@ -1,15 +1,16 @@
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% if request.user|follow_request_exists:user %} {% if request.user|follow_request_exists:user %}
<div class="field is-grouped"> <div class="field is-grouped">
<form action="/accept-follow-request/" method="POST"> <form action="/accept-follow-request/" method="POST">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ user.username }}"> <input type="hidden" name="user" value="{{ user.username }}">
<button class="button is-link is-small" type="submit">Accept</button> <button class="button is-link is-small" type="submit">{% trans "Accept" %}</button>
</form> </form>
<form action="/delete-follow-request/" method="POST"> <form action="/delete-follow-request/" method="POST">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ user.username }}"> <input type="hidden" name="user" value="{{ user.username }}">
<button class="button is-danger is-light is-small" type="submit" class="warning">Delete</button> <button class="button is-danger is-light is-small" type="submit" class="warning">{% trans "Delete" %}</button>
</form> </form>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,15 +1,16 @@
{% extends 'components/card.html' %} {% extends 'components/card.html' %}
{% load i18n %}
{% block card-header %} {% block card-header %}
<h3 class="card-header-title has-background-primary has-text-white"> <h3 class="card-header-title has-background-primary has-text-white">
<span class="icon icon-book is-size-3 mr-2" aria-hidden="true"></span> {{ year }} reading goal <span class="icon icon-book is-size-3 mr-2" aria-hidden="true"></span> {% blocktrans %}{{ year }} reading goal{% endblocktrans %}
</h3> </h3>
{% endblock %} {% endblock %}
{% block card-content %} {% block card-content %}
<div class="content"> <div class="content">
<p>Set a goal for how many books you'll finish reading in {{ year }}, and track your progress throughout the year.</p> <p>{% blocktrans %}Set a goal for how many books you'll finish reading in {{ year }}, and track your progress throughout the year.{% endblocktrans %}</p>
{% include 'snippets/goal_form.html' %} {% include 'snippets/goal_form.html' %}
</div> </div>
@ -17,7 +18,7 @@
{% block card-footer %} {% block card-footer %}
<div class="card-footer-item is-flex-direction-column"> <div class="card-footer-item is-flex-direction-column">
<button class="button is-danger is-light is-block set-display" data-id="hide-{{ year }}-reading-goal" data-value="true">Dismiss message</button> <button class="button is-danger is-light is-block set-display" data-id="hide-{{ year }}-reading-goal" data-value="true">{% trans "Dismiss message" %}</button>
<p class="help">You can set or change your reading goal any time from your <a href="{{ request.user.local_path }}">profile page</a></p> <p class="help">{% blocktrans with path=request.user.local_path %}You can set or change your reading goal any time from your <a href="{{ path }}">profile page</a>{% endblocktrans %}</p>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,3 +1,4 @@
{% load i18n %}
<form method="post" name="goal" action="{{ request.user.local_path }}/goal/{{ year }}"> <form method="post" name="goal" action="{{ request.user.local_path }}/goal/{{ year }}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="year" value="{% if goal %}{{ goal.year }}{% else %}{{ year }}{% endif %}"> <input type="hidden" name="year" value="{% if goal %}{{ goal.year }}{% else %}{{ year }}{% endif %}">
@ -5,28 +6,28 @@
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<label class="label" for="id_goal">Reading goal:</label> <label class="label" for="id_goal">{% trans "Reading goal:" %}</label>
<div class="field has-addons"> <div class="field has-addons">
<div class="control"> <div class="control">
<input type="number" class="input" name="goal" min="1" id="id_goal" value="{% if goal %}{{ goal.goal }}{% else %}12{% endif %}"> <input type="number" class="input" name="goal" min="1" id="id_goal" value="{% if goal %}{{ goal.goal }}{% else %}12{% endif %}">
</div> </div>
<p class="button is-static" aria-hidden="true">books</p> <p class="button is-static" aria-hidden="true">{% trans "books" %}</p>
</div> </div>
</div> </div>
<div class="column"> <div class="column">
<label class="label"><p class="mb-2">Goal privacy:</p> <label class="label"><p class="mb-2">{% trans "Goal privacy:" %}</p>
{% include 'snippets/privacy_select.html' with no_label=True current=goal.privacy %} {% include 'snippets/privacy_select.html' with no_label=True current=goal.privacy %}
</label> </label>
</div> </div>
</div> </div>
<label for="post_status" class="label"> <label for="post_status" class="label">
<input type="checkbox" name="post-status" id="post_status" class="checkbox" checked> <input type="checkbox" name="post-status" id="post_status" class="checkbox" checked>
Post to feed {% trans "Post to feed" %}
</label> </label>
<p> <p>
<button type="submit" class="button is-link">Set goal</button> <button type="submit" class="button is-link">{% trans "Set goal" %}</button>
{% if goal %} {% if goal %}
{% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="show-edit-goal" %} {% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="show-edit-goal" %}
{% endif %} {% endif %}

View file

@ -1,9 +1,10 @@
{% load i18n %}
{% load humanize %} {% load humanize %}
<p> <p>
{% if goal.progress_percent >= 100 %} {% if goal.progress_percent >= 100 %}
Success! {% trans "Success!" %}
{% elif goal.progress_percent %} {% elif goal.progress_percent %}
{{ goal.progress_percent }}% complete! {% blocktrans with percent=goal.percent %}{{ percent }}% complete!{% endblocktrans %}
{% endif %} {% endif %}
{% if goal.user == request.user %}You've{% else %}{{ goal.user.display_name }} has{% endif %} read {% if request.path != goal.local_path %}<a href="{{ goal.local_path }}">{% endif %}{{ goal.book_count }} of {{ goal.goal | intcomma }} books{% if request.path != goal.local_path %}</a>{% endif %}. {% if goal.user == request.user %}You've{% else %}{{ goal.user.display_name }} has{% endif %} read {% if request.path != goal.local_path %}<a href="{{ goal.local_path }}">{% endif %}{{ goal.book_count }} of {{ goal.goal | intcomma }} books{% if request.path != goal.local_path %}</a>{% endif %}.
</p> </p>

View file

@ -1,9 +1,10 @@
{% load i18n %}
<nav class="pagination" role="navigation" aria-label="pagination"> <nav class="pagination" role="navigation" aria-label="pagination">
{% if page.has_previous %} {% if page.has_previous %}
<p class="pagination-previous"> <p class="pagination-previous">
<a href="{{ path }}?page={{ page.previous_page_number }}{{ anchor }}"> <a href="{{ path }}?page={{ page.previous_page_number }}{{ anchor }}">
<span class="icon icon-arrow-left"></span> <span class="icon icon-arrow-left"></span>
Previous {% trans "Previous" %}
</a> </a>
</p> </p>
{% endif %} {% endif %}
@ -11,7 +12,7 @@
{% if page.has_next %} {% if page.has_next %}
<p class="pagination-next"> <p class="pagination-next">
<a href="{{ path }}?page={{ page.next_page_number }}{{ anchor }}"> <a href="{{ path }}?page={{ page.next_page_number }}{{ anchor }}">
Next {% trans "Next" %}
<span class="icon icon-arrow-right"></span> <span class="icon icon-arrow-right"></span>
</a> </a>
</p> </p>

View file

@ -1,18 +1,19 @@
{% load i18n %}
{% if item.privacy == 'public' %} {% if item.privacy == 'public' %}
<span class="icon icon-globe" title="Public"> <span class="icon icon-globe" title="{% trans 'Public' %}">
<span class="is-sr-only">Public</span> <span class="is-sr-only">{% trans "Public" %}</span>
</span> </span>
{% elif item.privacy == 'unlisted' %} {% elif item.privacy == 'unlisted' %}
<span class="icon icon-unlock" title="Unlisted"> <span class="icon icon-unlock" title="{% trans 'Unlisted' %}">
<span class="is-sr-only">Unlisted</span> <span class="is-sr-only">{% trans "Unlisted" %}</span>
</span> </span>
{% elif item.privacy == 'followers' %} {% elif item.privacy == 'followers' %}
<span class="icon icon-lock" title="Followers-only"> <span class="icon icon-lock" title="Followers-only">
<span class="is-sr-only">Followers-only</span> <span class="is-sr-only">{% trans "Followers-only" %}</span>
</span> </span>
{% else %} {% else %}
<span class="icon icon-envelope" title="Private"> <span class="icon icon-envelope" title="{% trans 'Private' %}">
<span class="is-sr-only">Private</span> <span class="is-sr-only">{% trans "Private" %}</span>
</span> </span>
{% endif %} {% endif %}

View file

@ -1,21 +1,22 @@
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
<div class="select {{ class }}"> <div class="select {{ class }}">
{% with 0|uuid as uuid %} {% with 0|uuid as uuid %}
{% if not no_label %} {% if not no_label %}
<label class="is-sr-only" for="privacy-{{ uuid }}">Post privacy</label> <label class="is-sr-only" for="privacy-{{ uuid }}">{% trans "Post privacy" %}</label>
{% endif %} {% endif %}
<select name="privacy" id="privacy-{{ uuid }}"> <select name="privacy" id="privacy-{{ uuid }}">
<option value="public" {% if not current or current == 'public' %}selected{% endif %}> <option value="public" {% if not current or current == 'public' %}selected{% endif %}>
Public {% trans "Public" %}
</option> </option>
<option value="unlisted" {% if current == 'unlisted' %}selected{% endif %}> <option value="unlisted" {% if current == 'unlisted' %}selected{% endif %}>
Unlisted {% trans "Unlisted" %}
</option> </option>
<option value="followers" {% if current == 'followers' %}selected{% endif %}> <option value="followers" {% if current == 'followers' %}selected{% endif %}>
Followers {% trans "Followers" %}
</option> </option>
<option value="direct" {% if current == 'direct' %}selected{% endif %}> <option value="direct" {% if current == 'direct' %}selected{% endif %}>
Private {% trans "Private" %}
</option> </option>
</select> </select>
{% endwith %} {% endwith %}

View file

@ -1,8 +1,9 @@
{% load i18n %}
<form class="field is-grouped is-small" action="/edit-readthrough" method="POST"> <form class="field is-grouped is-small" action="/edit-readthrough" method="POST">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="id" value="{{ readthrough.id }}"/> <input type="hidden" name="id" value="{{ readthrough.id }}"/>
<div class="field"> <div class="field">
<label class="label is-align-self-center mb-0 pr-2" for="progress">Progress:</label> <label class="label is-align-self-center mb-0 pr-2" for="progress">{% trans "Progress:" %}</label>
<div class="field has-addons mb-0"> <div class="field has-addons mb-0">
<div class="control"> <div class="control">
<input <input
@ -12,16 +13,16 @@
</div> </div>
<div class="control select is-small"> <div class="control select is-small">
<select name="progress_mode" aria-label="Progress mode"> <select name="progress_mode" aria-label="Progress mode">
<option value="PG" {% if readthrough.progress_mode == 'PG' %}selected{% endif %}>pages</option> <option value="PG" {% if readthrough.progress_mode == 'PG' %}selected{% endif %}>{% trans "pages" %}</option>
<option value="PCT" {% if readthrough.progress_mode == 'PCT' %}selected{% endif %}>percent</option> <option value="PCT" {% if readthrough.progress_mode == 'PCT' %}selected{% endif %}>{% trans "percent" %}</option>
</select> </select>
</div> </div>
<div class="control"> <div class="control">
<button class="button is-small px-2 is-primary" type="submit">Save</button> <button class="button is-small px-2 is-primary" type="submit">{% trans "Save" %}</button>
</div> </div>
</div> </div>
{% if readthrough.progress_mode == 'PG' and book.pages %} {% if readthrough.progress_mode == 'PG' and book.pages %}
<p class="help">of {{ book.pages }} pages</p> <p class="help">{% blocktrans %}of {{ book.pages }} pages{% endblocktrans %}</p>
{% endif %} {% endif %}
</div> </div>
</form> </form>

View file

@ -1,6 +1,7 @@
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<span class="is-sr-only">Leave a rating</span> <span class="is-sr-only">{% trans "Leave a rating" %}</span>
<div class="block"> <div class="block">
<form class="hidden-form" name="rate" action="/post/rating" method="POST"> <form class="hidden-form" name="rate" action="/post/rating" method="POST">
{% csrf_token %} {% csrf_token %}
@ -10,7 +11,7 @@
<input type="hidden" name="rating" value="{{ forloop.counter }}"> <input type="hidden" name="rating" value="{{ forloop.counter }}">
<div class="field is-grouped stars form-rate-stars mb-1 has-text-warning-dark"> <div class="field is-grouped stars form-rate-stars mb-1 has-text-warning-dark">
<label class="is-sr-only" for="rating-no-rating-{{ book.id }}">No rating</label> <label class="is-sr-only" for="rating-no-rating-{{ book.id }}">{% trans "No rating" %}</label>
<input class="is-sr-only" type="radio" name="rating" value="" id="rating-no-rating-{{ book.id }}" checked> <input class="is-sr-only" type="radio" name="rating" value="" id="rating-no-rating-{{ book.id }}" checked>
{% for i in '12345'|make_list %} {% for i in '12345'|make_list %}
<input class="is-sr-only" id="rating-book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}" {% if book|user_rating:user == forloop.counter %}checked{% endif %}> <input class="is-sr-only" id="rating-book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}" {% if book|user_rating:user == forloop.counter %}checked{% endif %}>
@ -25,7 +26,7 @@
{% include 'snippets/privacy_select.html' with class="is-small" %} {% include 'snippets/privacy_select.html' with class="is-small" %}
</div> </div>
<div class="control"> <div class="control">
<button class="button is-small is-primary" type="submit">Rate</button> <button class="button is-small is-primary" type="submit">{% trans "Rate" %}</button>
</div> </div>
</div> </div>
</form> </form>

View file

@ -1,13 +1,14 @@
{% load i18n %}
{% load humanize %} {% load humanize %}
<div class="content block"> <div class="content block">
<div id="hide-edit-readthrough-{{ readthrough.id }}"> <div id="hide-edit-readthrough-{{ readthrough.id }}">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
Progress Updates: {% trans "Progress Updates:" %}
</dl> </dl>
<ul> <ul>
{% if readthrough.finish_date or readthrough.progress %} {% if readthrough.finish_date or readthrough.progress %}
<li>{% if readthrough.finish_date %} {{ readthrough.finish_date | naturalday }}: finished {% else %}{% if readthrough.progress_mode == 'PG' %}on page {{ readthrough.progress }}{% if book.pages %} of {{ book.pages }}{% endif %} <li>{% if readthrough.finish_date %} {{ readthrough.finish_date | naturalday }}: {% trans "finished" %} {% else %}{% if readthrough.progress_mode == 'PG' %}on page {{ readthrough.progress }}{% if book.pages %} of {{ book.pages }}{% endif %}
{% else %}{{ readthrough.progress }}%{% endif %}{% endif %} {% else %}{{ readthrough.progress }}%{% endif %}{% endif %}
{% if readthrough.progress %} {% if readthrough.progress %}
{% include 'snippets/toggle/toggle_button.html' with text="Show all updates" controls_text="updates" controls_uid=readthrough.id class="is-small" %} {% include 'snippets/toggle/toggle_button.html' with text="Show all updates" controls_text="updates" controls_uid=readthrough.id class="is-small" %}
@ -25,7 +26,7 @@
<input type="hidden" name="id" value="{{ progress_update.id }}"/> <input type="hidden" name="id" value="{{ progress_update.id }}"/>
<button type="submit" class="button is-small" for="delete-progressupdate-{{ progress_update.id }}" role="button" tabindex="0"> <button type="submit" class="button is-small" for="delete-progressupdate-{{ progress_update.id }}" role="button" tabindex="0">
<span class="icon icon-x" title="Delete this progress update"> <span class="icon icon-x" title="Delete this progress update">
<span class="is-sr-only">Delete this progress update</span> <span class="is-sr-only">{% trans "Delete this progress update" %}</span>
</span> </span>
</button> </button>
</form> </form>
@ -35,7 +36,7 @@
{% endif %} {% endif %}
</li> </li>
{% endif %} {% endif %}
<li>{{ readthrough.start_date | naturalday }}: started</li> <li>{{ readthrough.start_date | naturalday }}: {% trans "started" %}</li>
</ul> </ul>
</div> </div>
<div class="column is-narrow"> <div class="column is-narrow">
@ -53,11 +54,11 @@
</div> </div>
<div class="box hidden" id="edit-readthrough-{{ readthrough.id }}" tabindex="0"> <div class="box hidden" id="edit-readthrough-{{ readthrough.id }}" tabindex="0">
<h3 class="title is-5">Edit read dates</h3> <h3 class="title is-5">{% trans "Edit read dates" %}</h3>
<form name="edit-readthrough" action="/edit-readthrough" method="post"> <form name="edit-readthrough" action="/edit-readthrough" method="post">
{% include 'snippets/readthrough_form.html' with readthrough=readthrough %} {% include 'snippets/readthrough_form.html' with readthrough=readthrough %}
<div class="field is-grouped"> <div class="field is-grouped">
<button class="button is-primary" type="submit">Save</button> <button class="button is-primary" type="submit">{% trans "Save" %}</button>
{% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="edit-readthrough" controls_uid=readthrough.id %} {% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="edit-readthrough" controls_uid=readthrough.id %}
</div> </div>
</form> </form>

View file

@ -1,16 +1,17 @@
{% load i18n %}
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="id" value="{{ readthrough.id }}"> <input type="hidden" name="id" value="{{ readthrough.id }}">
<input type="hidden" name="book" value="{{ book.id }}"> <input type="hidden" name="book" value="{{ book.id }}">
<div class="field"> <div class="field">
<label class="label"> <label class="label">
Started reading {% trans "Started reading" %}
<input type="date" name="start_date" class="input" id="id_start_date-{{ readthrough.id }}" value="{{ readthrough.start_date | date:"Y-m-d" }}"> <input type="date" name="start_date" class="input" id="id_start_date-{{ readthrough.id }}" value="{{ readthrough.start_date | date:"Y-m-d" }}">
</label> </label>
</div> </div>
{# Only show progress for editing existing readthroughs #} {# Only show progress for editing existing readthroughs #}
{% if readthrough.id and not readthrough.finish_date %} {% if readthrough.id and not readthrough.finish_date %}
<label class="label" for="id_progress-{{ readthrough.id }}"> <label class="label" for="id_progress-{{ readthrough.id }}">
Progress {% trans "Progress" %}
</label> </label>
<div class="field has-addons"> <div class="field has-addons">
<div class="control"> <div class="control">
@ -18,15 +19,15 @@
</div> </div>
<div class="control select"> <div class="control select">
<select name="progress_mode" aria-label="Progress mode"> <select name="progress_mode" aria-label="Progress mode">
<option value="PG" {% if readthrough.progress_mode == 'PG' %}selected{% endif %}>pages</option> <option value="PG" {% if readthrough.progress_mode == 'PG' %}selected{% endif %}>{% trans "pages" %}</option>
<option value="PCT" {% if readthrough.progress_mode == 'PCT' %}selected{% endif %}>percent</option> <option value="PCT" {% if readthrough.progress_mode == 'PCT' %}selected{% endif %}>{% trans "percent" %}</option>
</select> </select>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="field"> <div class="field">
<label class="label"> <label class="label">
Finished reading {% trans "Finished reading" %}
<input type="date" name="finish_date" class="input" id="id_finish_date-{{ readthrough.id }}" value="{{ readthrough.finish_date | date:"Y-m-d" }}"> <input type="date" name="finish_date" class="input" id="id_finish_date-{{ readthrough.id }}" value="{{ readthrough.finish_date | date:"Y-m-d" }}">
</label> </label>
</div> </div>

View file

@ -1,6 +1,7 @@
{% load i18n %}
{% csrf_token %} {% csrf_token %}
<div class="field"> <div class="field">
<label class="label" for="id_localname_register">Username:</label> <label class="label" for="id_localname_register">{% trans "Username:" %}</label>
<div class="control"> <div class="control">
<input type="text" name="localname" maxlength="150" class="input" required="" id="id_localname_register" value="{% if register_form.localname.value %}{{ register_form.localname.value }}{% endif %}"> <input type="text" name="localname" maxlength="150" class="input" required="" id="id_localname_register" value="{% if register_form.localname.value %}{{ register_form.localname.value }}{% endif %}">
</div> </div>
@ -9,7 +10,7 @@
{% endfor %} {% endfor %}
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="id_email_register">Email address:</label> <label class="label" for="id_email_register">{% trans "Email address:" %}</label>
<div class="control"> <div class="control">
<input type="email" name="email" maxlength="254" class="input" id="id_email_register" value="{% if register_form.email.value %}{{ register_form.email.value }}{% endif %}"> <input type="email" name="email" maxlength="254" class="input" id="id_email_register" value="{% if register_form.email.value %}{{ register_form.email.value }}{% endif %}">
{% for error in register_form.email.errors %} {% for error in register_form.email.errors %}
@ -18,7 +19,7 @@
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label" for="id_password_register">Password:</label> <label class="label" for="id_password_register">{% trans "Password:" %}</label>
<div class="control"> <div class="control">
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password_register"> <input type="password" name="password" maxlength="128" class="input" required="" id="id_password_register">
{% for error in register_form.password.errors %} {% for error in register_form.password.errors %}
@ -28,6 +29,6 @@
</div> </div>
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
<button class="button is-primary" type="submit">Sign Up</button> <button class="button is-primary" type="submit">{% trans "Sign Up" %}</button>
</div> </div>
</div> </div>

View file

@ -1,13 +1,14 @@
{% load i18n %}
{{ obj.user.display_name }}{% if obj.status_type == 'GeneratedNote' %} {{ obj.user.display_name }}{% if obj.status_type == 'GeneratedNote' %}
{{ obj.content | safe }} {{ obj.content | safe }}
{% elif obj.status_type == 'Review' and not obj.name and not obj.content%} {% elif obj.status_type == 'Review' and not obj.name and not obj.content%}
rated {% trans "rated" %}
{% elif obj.status_type == 'Review' %} {% elif obj.status_type == 'Review' %}
reviewed {% trans "reviewed" %}
{% elif obj.status_type == 'Comment' %} {% elif obj.status_type == 'Comment' %}
commented on {% trans "commented on" %}
{% elif obj.status_type == 'Quotation' %} {% elif obj.status_type == 'Quotation' %}
quoted {% trans "quoted" %}
{% endif %} {% endif %}
{% if obj.book %}{{ obj.book.title | safe}} {% if obj.book %}{{ obj.book.title | safe}}
{% elif obj.mention_books %} {% elif obj.mention_books %}

View file

@ -1,2 +1,3 @@
{% load i18n %}
<strong>{% if link %}<a href="{{ result.key }}">{{ result.title }}</a>{% else %}{{ result.title }}{% endif %}</strong> <strong>{% if link %}<a href="{{ result.key }}">{{ result.title }}</a>{% else %}{{ result.title }}{% endif %}</strong>
{% if result.author %} by {{ result.author }}{% endif %}{% if result.year %} ({{ result.year }}){% endif %} {% if result.author %} {% blocktrans with author=result.author %}by {{ author }}{% endblocktrans %}{% endif %}{% if result.year %} ({{ result.year }}){% endif %}

View file

@ -1,39 +1,20 @@
{% load humanize %} {% load humanize %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% if books|length > 0 %} {% if books|length > 0 %}
<div class="table-container"> <div class="table-container">
<table class="table is-striped is-fullwidth"> <table class="table is-striped is-fullwidth">
<tr class="book-preview"> <tr class="book-preview">
<th> <th>{% trans "Cover" %}</th>
Cover <th>{% trans "Title" %}</th>
</th> <th>{% trans "Author" %}</th>
<th> <th>{% trans "Published" %}</th>
Title <th>{% trans "Shelved" %}</th>
</th> <th>{% trans "Started" %}</th>
<th> <th>{% trans "Finished" %}</th>
Author <th>{% trans "External links" %}</th>{% if ratings %}
</th> <th>{% trans "Rating" %}</th>{% endif %}
<th>
Published
</th>
<th>
Shelved
</th>
<th>
Started
</th>
<th>
Finished
</th>
<th>
External links
</th>
{% if ratings %}
<th>
Rating
</th>
{% endif %}
</tr> </tr>
{% for book in books %} {% for book in books %}
<tr class="book-preview"> <tr class="book-preview">
@ -60,7 +41,7 @@
{{ read_through.finish_date | naturalday |default_if_none:""}} {{ read_through.finish_date | naturalday |default_if_none:""}}
</td> </td>
<td> <td>
<a href="https://openlibrary.org/book/{{ book.openlibrary_key }}" target="_blank">OpenLibrary</a> <a href="https://openlibrary.org/book/{{ book.openlibrary_key }}" target="_blank">{% trans "OpenLibrary" %}</a>
</td> </td>
{% if ratings %} {% if ratings %}
<td> <td>
@ -77,13 +58,13 @@
</table> </table>
</div> </div>
{% else %} {% else %}
<p>This shelf is empty.</p> <p>{% trans "This shelf is empty." %}</p>
{% if shelf.editable %} {% if shelf.editable %}
<form name="delete-shelf" action="/delete-shelf/{{ shelf.id }}" method="post"> <form name="delete-shelf" action="/delete-shelf/{{ shelf.id }}" method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}"> <input type="hidden" name="user" value="{{ request.user.id }}">
<button class="button is-danger is-light" type="submit"> <button class="button is-danger is-light" type="submit">
Delete shelf {% trans "Delete shelf" %}
</button> </button>
</form> </form>
{% endif %} {% endif %}

View file

@ -1,6 +1,7 @@
{% extends 'components/dropdown.html' %} {% extends 'components/dropdown.html' %}
{% load i18n %}
{% block dropdown-trigger %} {% block dropdown-trigger %}
<span>Change shelf</span> <span>{% trans "Change shelf" %}</span>
<span class="icon icon-arrow-down" aria-hidden="true"></span> <span class="icon icon-arrow-down" aria-hidden="true"></span>
{% endblock %} {% endblock %}
@ -23,7 +24,7 @@
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}"> <input type="hidden" name="book" value="{{ book.id }}">
<input type="hidden" name="shelf" value="{{ current.id }}"> <input type="hidden" name="shelf" value="{{ current.id }}">
<button class="button is-fullwidth is-small is-danger is-light" type="submit">Unshelve</button> <button class="button is-fullwidth is-small is-danger is-light" type="submit">{% trans "Unshelve" %}</button>
</form> </form>
</li> </li>
{% endblock %} {% endblock %}

View file

@ -1,7 +1,8 @@
{% extends 'components/modal.html' %} {% extends 'components/modal.html' %}
{% load i18n %}
{% block modal-title %} {% block modal-title %}
Finish "<em>{{ book.title }}</em>" {% blocktrans with book_title=book.title %}Finish "<em>{{ book_title }}</em>"{% endblocktrans %}
{% endblock %} {% endblock %}
@ -15,13 +16,13 @@ Finish "<em>{{ book.title }}</em>"
<input type="hidden" name="id" value="{{ readthrough.id }}"> <input type="hidden" name="id" value="{{ readthrough.id }}">
<div class="field"> <div class="field">
<label class="label"> <label class="label">
Started reading {% trans "Started reading" %}
<input type="date" name="start_date" class="input" id="finish_id_start_date-{{ uuid }}" value="{{ readthrough.start_date | date:"Y-m-d" }}"> <input type="date" name="start_date" class="input" id="finish_id_start_date-{{ uuid }}" value="{{ readthrough.start_date | date:"Y-m-d" }}">
</label> </label>
</div> </div>
<div class="field"> <div class="field">
<label class="label"> <label class="label">
Finished reading {% trans "Finished reading" %}
<input type="date" name="finish_date" class="input" id="id_finish_date-{{ uuid }}" value="{% now "Y-m-d" %}"> <input type="date" name="finish_date" class="input" id="id_finish_date-{{ uuid }}" value="{% now "Y-m-d" %}">
</label> </label>
</div> </div>
@ -33,12 +34,12 @@ Finish "<em>{{ book.title }}</em>"
<div class="column field"> <div class="column field">
<label for="post_status-{{ uuid }}"> <label for="post_status-{{ uuid }}">
<input type="checkbox" name="post-status" class="checkbox" id="post_status-{{ uuid }}" checked> <input type="checkbox" name="post-status" class="checkbox" id="post_status-{{ uuid }}" checked>
Post to feed {% trans "Post to feed" %}
</label> </label>
{% include 'snippets/privacy_select.html' %} {% include 'snippets/privacy_select.html' %}
</div> </div>
<div class="column"> <div class="column">
<button type="submit" class="button is-success">Save</button> <button type="submit" class="button is-success">{% trans "Save" %}</button>
{% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="finish-reading" controls_uid=uuid %} {% include 'snippets/toggle/close_button.html' with text="Cancel" controls_text="finish-reading" controls_uid=uuid %}
</div> </div>
</div> </div>

View file

@ -1,7 +1,8 @@
{% extends 'components/dropdown.html' %} {% extends 'components/dropdown.html' %}
{% load i18n %}
{% block dropdown-trigger %} {% block dropdown-trigger %}
<span class="icon icon-arrow-down"> <span class="icon icon-arrow-down">
<span class="is-sr-only">More shelves</span> <span class="is-sr-only">{% trans "More shelves" %}</span>
</span> </span>
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
{% for shelf in shelves %} {% for shelf in shelves %}
{% comparison_bool shelf.identifier active_shelf.shelf.identifier as is_current %} {% comparison_bool shelf.identifier active_shelf.shelf.identifier as is_current %}
{% if dropdown %}<li role="menuitem">{% endif %} {% if dropdown %}<li role="menuitem">{% endif %}
@ -6,7 +7,7 @@
{% if shelf.identifier == 'reading' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %} {% if shelf.identifier == 'reading' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
{% include 'snippets/toggle/toggle_button.html' with class=class text="Start reading" controls_text="start-reading" controls_uid=button_uuid focus="modal-title-start-reading" disabled=is_current %} {% include 'snippets/toggle/toggle_button.html' with class=class text="Start reading" controls_text="start-reading" controls_uid=button_uuid focus="modal-title-start-reading" disabled=is_current %}
{% endif %}{% elif shelf.identifier == 'read' and active_shelf.shelf.identifier == 'read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %} {% endif %}{% elif shelf.identifier == 'read' and active_shelf.shelf.identifier == 'read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
<button type="button" class="button {{ class }}" disabled><span>Read</span> <button type="button" class="button {{ class }}" disabled><span>{% trans "Read" %}</span>
{% endif %}{% elif shelf.identifier == 'read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %} {% endif %}{% elif shelf.identifier == 'read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
{% include 'snippets/toggle/toggle_button.html' with class=class text="Finish reading" controls_text="finish-reading" controls_uid=button_uuid focus="modal-title-finish-reading" disabled=is_current %} {% include 'snippets/toggle/toggle_button.html' with class=class text="Finish reading" controls_text="finish-reading" controls_uid=button_uuid focus="modal-title-finish-reading" disabled=is_current %}
{% endif %}{% elif shelf.identifier == 'to-read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %} {% endif %}{% elif shelf.identifier == 'to-read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}

View file

@ -1,7 +1,8 @@
{% extends 'components/modal.html' %} {% extends 'components/modal.html' %}
{% load i18n %}
{% block modal-title %} {% block modal-title %}
Start "<em>{{ book.title }}</em>" {% blocktrans with book_title=book.title %}Start "<em>{{ book_title }}</em>"{% endblocktrans %}
{% endblock %} {% endblock %}
{% block modal-form-open %} {% block modal-form-open %}
@ -13,7 +14,7 @@ Start "<em>{{ book.title }}</em>"
{% csrf_token %} {% csrf_token %}
<div class="field"> <div class="field">
<label class="label"> <label class="label">
Started reading {% trans "Started reading" %}
<input type="date" name="start_date" class="input" id="start_id_start_date-{{ uuid }}" value="{% now "Y-m-d" %}"> <input type="date" name="start_date" class="input" id="start_id_start_date-{{ uuid }}" value="{% now "Y-m-d" %}">
</label> </label>
</div> </div>
@ -25,12 +26,12 @@ Start "<em>{{ book.title }}</em>"
<div class="column field"> <div class="column field">
<label for="post_status_start-{{ uuid }}"> <label for="post_status_start-{{ uuid }}">
<input type="checkbox" name="post-status" class="checkbox" id="post_status_start-{{ uuid }}" checked> <input type="checkbox" name="post-status" class="checkbox" id="post_status_start-{{ uuid }}" checked>
Post to feed {% trans "Post to feed" %}
</label> </label>
{% include 'snippets/privacy_select.html' %} {% include 'snippets/privacy_select.html' %}
</div> </div>
<div class="column"> <div class="column">
<button class="button is-success" type="submit">Save</button> <button class="button is-success" type="submit">{% trans "Save" %}</button>
{% include 'snippets/toggle/toggle_button.html' with text="Cancel" controls_text="start-reading" controls_uid=uuid %} {% include 'snippets/toggle/toggle_button.html' with text="Cancel" controls_text="start-reading" controls_uid=uuid %}
</div> </div>
</div> </div>

View file

@ -1,7 +1,8 @@
{% extends 'components/modal.html' %} {% extends 'components/modal.html' %}
{% load i18n %}
{% block modal-title %} {% block modal-title %}
Want to Read "<em>{{ book.title }}</em>" {% blocktrans with book_title=book.title %}Want to Read "<em>{{ book_title }}</em>"{% endblocktrans %}
{% endblock %} {% endblock %}
{% block modal-form-open %} {% block modal-form-open %}
@ -16,13 +17,13 @@ Want to Read "<em>{{ book.title }}</em>"
<div class="column field"> <div class="column field">
<label for="post_status_want-{{ uuid }}"> <label for="post_status_want-{{ uuid }}">
<input type="checkbox" name="post-status" class="checkbox" id="post_status_want-{{ uuid }}" checked> <input type="checkbox" name="post-status" class="checkbox" id="post_status_want-{{ uuid }}" checked>
Post to feed {% trans "Post to feed" %}
</label> </label>
{% include 'snippets/privacy_select.html' %} {% include 'snippets/privacy_select.html' %}
</div> </div>
<div class="column"> <div class="column">
<button class="button is-success" type="submit"> <button class="button is-success" type="submit">
<span>Want to read</span> <span>{% trans "Want to read" %}</span>
</button> </button>
{% include 'snippets/toggle/toggle_button.html' with text="Cancel" controls_text="want-to-read" controls_uid=uuid %} {% include 'snippets/toggle/toggle_button.html' with text="Cancel" controls_text="want-to-read" controls_uid=uuid %}
</div> </div>

View file

@ -1,5 +1,6 @@
{% load i18n %}
<p class="stars"> <p class="stars">
<span class="is-sr-only">{% if rating %}{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}{% else %}No rating{% endif %}</span> <span class="is-sr-only">{% if rating %}{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}{% else %}{% trans "No rating" %}{% endif %}</span>
{% for i in '12345'|make_list %} {% for i in '12345'|make_list %}
<span class="icon is-small mr-1 icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}" aria-hidden="true"> <span class="icon is-small mr-1 icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}" aria-hidden="true">
</span> </span>

View file

@ -1,9 +1,10 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
{% if not status.deleted %} {% if not status.deleted %}
{% if status.status_type == 'Boost' %} {% if status.status_type == 'Boost' %}
{% include 'snippets/avatar.html' with user=status.user %} {% include 'snippets/avatar.html' with user=status.user %}
{% include 'snippets/username.html' with user=status.user %} {% include 'snippets/username.html' with user=status.user %}
boosted {% trans "boosted" %}
{% include 'snippets/status/status_body.html' with status=status|boosted_status %} {% include 'snippets/status/status_body.html' with status=status|boosted_status %}
{% else %} {% else %}
{% include 'snippets/status/status_body.html' with status=status %} {% include 'snippets/status/status_body.html' with status=status %}

View file

@ -1,4 +1,5 @@
{% extends 'components/card.html' %} {% extends 'components/card.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load humanize %} {% load humanize %}
@ -32,16 +33,16 @@
{% else %} {% else %}
<a href="/login"> <a href="/login">
<span class="icon icon-comment" title="Reply"> <span class="icon icon-comment" title="{% trans 'Reply' %}">
<span class="is-sr-only">Reply</span> <span class="is-sr-only">{% trans "Reply" %}</span>
</span> </span>
<span class="icon icon-boost" title="Boost status"> <span class="icon icon-boost" title="{% trans 'Boost status' %}">
<span class="is-sr-only">Boost status</span> <span class="is-sr-only">{% trans "Boost status" %}</span>
</span> </span>
<span class="icon icon-heart" title="Like status"> <span class="icon icon-heart" title="{% trans 'Like status' %}">
<span class="is-sr-only">Like status</span> <span class="is-sr-only">{% trans "Like status" %}</span>
</span> </span>
</a> </a>
{% endif %} {% endif %}

View file

@ -1,4 +1,5 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
<div class="block"> <div class="block">
{% if status.status_type == 'Review' %} {% if status.status_type == 'Review' %}
<div> <div>
@ -38,7 +39,7 @@
{% for attachment in status.attachments.all %} {% for attachment in status.attachments.all %}
<div class="column is-narrow"> <div class="column is-narrow">
<figure class="image is-128x128"> <figure class="image is-128x128">
<a href="/images/{{ attachment.image }}" target="_blank" aria-label="open image in new window"> <a href="/images/{{ attachment.image }}" target="_blank" aria-label="{% trans 'Open image in new window' %}">
<img src="/images/{{ attachment.image }}"{% if attachment.caption %} alt="{{ attachment.caption }}" title="{{ attachment.caption }}"{% endif %}> <img src="/images/{{ attachment.image }}"{% if attachment.caption %} alt="{{ attachment.caption }}" title="{{ attachment.caption }}"{% endif %}>
</a> </a>
</figure> </figure>

View file

@ -1,17 +1,18 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
{% include 'snippets/avatar.html' with user=status.user %} {% include 'snippets/avatar.html' with user=status.user %}
{% include 'snippets/username.html' with user=status.user %} {% include 'snippets/username.html' with user=status.user %}
{% if status.status_type == 'GeneratedNote' %} {% if status.status_type == 'GeneratedNote' %}
{{ status.content | safe }} {{ status.content | safe }}
{% elif status.status_type == 'Review' and not status.name and not status.content%} {% elif status.status_type == 'Review' and not status.name and not status.content%}
rated {% trans "rated" %}
{% elif status.status_type == 'Review' %} {% elif status.status_type == 'Review' %}
reviewed {% trans "reviewed" %}
{% elif status.status_type == 'Comment' %} {% elif status.status_type == 'Comment' %}
commented on {% trans "commented on" %}
{% elif status.status_type == 'Quotation' %} {% elif status.status_type == 'Quotation' %}
quoted {% trans "quoted" %}
{% elif status.reply_parent %} {% elif status.reply_parent %}
{% with parent_status=status|parent %} {% with parent_status=status|parent %}
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.remote_id }}">{% if parent_status.status_type == 'GeneratedNote' %}update{% else %}{{ parent_status.status_type | lower }}{% endif %}</a> replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.remote_id }}">{% if parent_status.status_type == 'GeneratedNote' %}update{% else %}{{ parent_status.status_type | lower }}{% endif %}</a>

View file

@ -1,9 +1,10 @@
{% extends 'components/dropdown.html' %} {% extends 'components/dropdown.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block dropdown-trigger %} {% block dropdown-trigger %}
<span class="icon icon-dots-three"> <span class="icon icon-dots-three">
<span class="is-sr-only">More options</span> <span class="is-sr-only">{% trans "More options" %}</span>
</span> </span>
{% endblock %} {% endblock %}
@ -13,13 +14,13 @@
<form class="dropdown-item pt-0 pb-0" name="delete-{{status.id}}" action="/delete-status/{{ status.id }}" method="post"> <form class="dropdown-item pt-0 pb-0" name="delete-{{status.id}}" action="/delete-status/{{ status.id }}" method="post">
{% csrf_token %} {% csrf_token %}
<button class="button is-danger is-light is-fullwidth is-small" type="submit"> <button class="button is-danger is-light is-fullwidth is-small" type="submit">
Delete post {% trans "Delete post" %}
</button> </button>
</form> </form>
</li> </li>
{% else %} {% else %}
<li role="menuitem"> <li role="menuitem">
<a href="/direct-messages/{{ status.user|username }}" class="button is-fullwidth is-small">Send direct message</a> <a href="/direct-messages/{{ status.user|username }}" class="button is-fullwidth is-small">{% trans "Send direct message" %}</a>
</li> </li>
<li role="menuitem"> <li role="menuitem">
{% include 'snippets/block_button.html' with user=status.user class="is-fullwidth" %} {% include 'snippets/block_button.html' with user=status.user class="is-fullwidth" %}

View file

@ -1,5 +1,6 @@
{% load i18n %}
<form name="switch-edition" action="/switch-edition" method="POST"> <form name="switch-edition" action="/switch-edition" method="POST">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="edition" value="{{ edition.id }}"> <input type="hidden" name="edition" value="{{ edition.id }}">
<button type="submit" class="button {{ size }}">Switch to this edition</button> <button type="submit" class="button {{ size }}">{% trans "Switch to this edition" %}</button>
</form> </form>

View file

@ -1,3 +1,4 @@
{% load i18n %}
<div class="control"> <div class="control">
<form name="tag" action="/{% if tag.tag.identifier in user_tags %}untag{% else %}tag{% endif %}/" method="post"> <form name="tag" action="/{% if tag.tag.identifier in user_tags %}untag{% else %}tag{% endif %}/" method="post">
{% csrf_token %} {% csrf_token %}
@ -10,11 +11,11 @@
</a> </a>
{% if tag.tag.identifier in user_tags %} {% if tag.tag.identifier in user_tags %}
<button class="tag is-delete" type="submit"> <button class="tag is-delete" type="submit">
<span class="is-sr-only">remove tag</span> <span class="is-sr-only">{% trans "Remove tag" %}</span>
</button> </button>
{% else %} {% else %}
<button class="tag" type="submit">+ <button class="tag" type="submit">+
<span class="is-sr-only">add tag</span> <span class="is-sr-only">{% trans "Add tag" %}</span>
</button> </button>
{% endif %} {% endif %}
</div> </div>

View file

@ -1,15 +1,16 @@
{% extends 'components/dropdown.html' %} {% extends 'components/dropdown.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block dropdown-trigger %} {% block dropdown-trigger %}
<span class="icon icon-dots-three"> <span class="icon icon-dots-three">
<span class="is-sr-only">More options</span> <span class="is-sr-only">{% trans "More options" %}</span>
</span> </span>
{% endblock %} {% endblock %}
{% block dropdown-list %} {% block dropdown-list %}
<li role="menuitem"> <li role="menuitem">
<a href="/direct-messages/{{ user|username }}" class="button is-fullwidth is-small">Send direct message</a> <a href="/direct-messages/{{ user|username }}" class="button is-fullwidth is-small">{% trans "Send direct message" %}</a>
</li> </li>
<li role="menuitem"> <li role="menuitem">
{% include 'snippets/block_button.html' with user=user class="is-fullwidth" %} {% include 'snippets/block_button.html' with user=user class="is-fullwidth" %}

View file

@ -1,9 +1,10 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block content %} {% block content %}
<div class="block"> <div class="block">
<h1 class="title">Books tagged "{{ tag.name }}"</h1> <h1 class="title">{% blocktrans %}Books tagged "{{ tag.name }}"{% endblocktrans %}</h1>
{% include 'snippets/book_tiles.html' with books=books.all %} {% include 'snippets/book_tiles.html' with books=books.all %}
</div> </div>

View file

@ -1,7 +1,8 @@
{% extends 'components/inline_form.html' %} {% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %} {% block header %}
Create New Shelf {% trans "Create New Shelf" %}
{% endblock %} {% endblock %}
{% block form %} {% block form %}
@ -9,7 +10,7 @@ Create New Shelf
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}"> <input type="hidden" name="user" value="{{ request.user.id }}">
<div class="field"> <div class="field">
<label class="label" for="id_name_create">Name:</label> <label class="label" for="id_name_create">{% trans "Name:" %}</label>
<input type="text" name="name" maxlength="100" class="input" required="true" id="id_name_create"> <input type="text" name="name" maxlength="100" class="input" required="true" id="id_name_create">
</div> </div>
@ -18,7 +19,7 @@ Create New Shelf
{% include 'snippets/privacy_select.html' %} {% include 'snippets/privacy_select.html' %}
</div> </div>
<div class="control"> <div class="control">
<button class="button is-primary" type="submit">Create shelf</button> <button class="button is-primary" type="submit">{% trans "Create shelf" %}</button>
</div> </div>
</div> </div>
</form> </form>

View file

@ -1,7 +1,8 @@
{% extends 'components/inline_form.html' %} {% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %} {% block header %}
Edit Shelf {% trans "Edit Shelf" %}
{% endblock %} {% endblock %}
{% block form %} {% block form %}
@ -10,7 +11,7 @@ Edit Shelf
<input type="hidden" name="user" value="{{ request.user.id }}"> <input type="hidden" name="user" value="{{ request.user.id }}">
{% if shelf.editable %} {% if shelf.editable %}
<div class="field"> <div class="field">
<label class="label" for="id_name">Name:</label> <label class="label" for="id_name">{% trans "Name:" %}</label>
<input type="text" name="name" maxlength="100" class="input" required="true" value="{{ shelf.name }}" id="id_name"> <input type="text" name="name" maxlength="100" class="input" required="true" value="{{ shelf.name }}" id="id_name">
</div> </div>
{% else %} {% else %}
@ -22,7 +23,7 @@ Edit Shelf
{% include 'snippets/privacy_select.html' with current=shelf.privacy %} {% include 'snippets/privacy_select.html' with current=shelf.privacy %}
</div> </div>
<div class="control"> <div class="control">
<button class="button is-primary" type="submit">Update shelf</button> <button class="button is-primary" type="submit">{% trans "Update shelf" %}</button>
</div> </div>
</div> </div>
</form> </form>

View file

@ -1,4 +1,5 @@
{% extends 'user/user_layout.html' %} {% extends 'user/user_layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block header %} {% block header %}
@ -13,7 +14,7 @@
{% block panel %} {% block panel %}
<div class="block"> <div class="block">
<h2 class="title">Followers</h2> <h2 class="title">{% trans "Followers" %}</h2>
{% for followers in followers %} {% for followers in followers %}
<div class="block columns"> <div class="block columns">
<div class="column"> <div class="column">
@ -26,7 +27,7 @@
</div> </div>
{% endfor %} {% endfor %}
{% if not followers.count %} {% if not followers.count %}
<div>{{ user|username }} has no followers</div> <div>{% blocktrans with username=user|username %}{{ username }} has no followers{% endblocktrans %}</div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
{% extends 'user/user_layout.html' %} {% extends 'user/user_layout.html' %}
{% load i18n %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% block header %} {% block header %}
@ -13,7 +14,7 @@
{% block panel %} {% block panel %}
<div class="block"> <div class="block">
<h2 class="title">Following</h2> <h2 class="title">{% trans "Following" %}</h2>
{% for follower in user.following.all %} {% for follower in user.following.all %}
<div class="block columns"> <div class="block columns">
<div class="column"> <div class="column">
@ -26,7 +27,7 @@
</div> </div>
{% endfor %} {% endfor %}
{% if not following.count %} {% if not following.count %}
<div>{{ user|username }} isn't following any users</div> <div>{% blocktrans with username=user|username %}{{ username }} isn't following any users{% endblocktrans %}</div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
{% extends 'user/user_layout.html' %} {% extends 'user/user_layout.html' %}
{% load i18n %}
{% block header %} {% block header %}
<div class="columns is-mobile"> <div class="columns is-mobile">
@ -24,7 +25,7 @@
<section class="block content"> <section class="block content">
<form name="create-list" method="post" action="{% url 'lists' %}" class="box hidden" id="create-list"> <form name="create-list" method="post" action="{% url 'lists' %}" class="box hidden" id="create-list">
<header class="columns"> <header class="columns">
<h3 class="title column">Create list</h3> <h3 class="title column">{% trans "Create list" %}</h3>
<div class="column is-narrow"> <div class="column is-narrow">
{% include 'snippets/toggle/toggle_button.html' with controls_text="create-list" label="close" class="delete" nonbutton=True %} {% include 'snippets/toggle/toggle_button.html' with controls_text="create-list" label="close" class="delete" nonbutton=True %}
</div> </div>

View file

@ -1,15 +1,16 @@
{% extends 'user/user_layout.html' %} {% extends 'user/user_layout.html' %}
{% load i18n %}
{% block header %} {% block header %}
<div class="columns is-mobile"> <div class="columns is-mobile">
<div class="column"> <div class="column">
<h1 class="title">User profile</h1> <h1 class="title">{% trans "User profile" %}</h1>
</div> </div>
{% if is_self %} {% if is_self %}
<div class="column is-narrow"> <div class="column is-narrow">
<a href="/preferences/profile"> <a href="/preferences/profile">
<span class="icon icon-pencil" title="Edit profile"> <span class="icon icon-pencil" title="Edit profile">
<span class="is-sr-only">Edit profile</span> <span class="is-sr-only">{% trans "Edit profile" %}</span>
</span> </span>
</a> </a>
</div> </div>
@ -20,12 +21,12 @@
{% block panel %} {% block panel %}
{% if user.bookwyrm_user %} {% if user.bookwyrm_user %}
<div class="block"> <div class="block">
<h2 class="title">Shelves</h2> <h2 class="title">{% trans "Shelves" %}</h2>
<div class="columns"> <div class="columns">
{% for shelf in shelves %} {% for shelf in shelves %}
<div class="column is-narrow"> <div class="column is-narrow">
<h3>{{ shelf.name }} <h3>{{ shelf.name }}
{% if shelf.size > 3 %}<small>(<a href="{{ shelf.local_path }}">See all {{ shelf.size }}</a>)</small>{% endif %}</h3> {% if shelf.size > 3 %}<small>(<a href="{{ shelf.local_path }}">{% blocktrans with size=shelf.size %}See all {{ size }}{% endblocktrans %}</a>)</small>{% endif %}</h3>
<div class="is-mobile field is-grouped"> <div class="is-mobile field is-grouped">
{% for book in shelf.books %} {% for book in shelf.books %}
<div class="control"> <div class="control">
@ -38,7 +39,7 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<small><a href="{{ user.local_path }}/shelves">See all {{ shelf_count }} shelves</a></small> <small><a href="{{ user.local_path }}/shelves">{% blocktrans %}See all {{ shelf_count }} shelves{% endblocktrans %}</a></small>
</div> </div>
{% endif %} {% endif %}
@ -49,16 +50,17 @@
</div> </div>
{% elif user == request.user %} {% elif user == request.user %}
<div class="block"> <div class="block">
<h2 class="title is-4"><a href="{{ user.local_path }}/goal/{% now 'Y' %}">Set a reading goal for {% now 'Y' %}</a></h2> {% now 'Y' as year %}
<h2 class="title is-4"><a href="{{ user.local_path }}/goal/{{ year }}">{% blocktrans %}Set a reading goal for {{ year }}{% endblocktrans %}</a></h2>
</div> </div>
{% endif %} {% endif %}
<div> <div>
<div class="columns is-mobile"> <div class="columns is-mobile">
<h2 class="title column">User Activity</h2> <h2 class="title column">{% trans "User Activity" %}</h2>
<div class="column is-narrow"> <div class="column is-narrow">
<a class="icon icon-rss" target="_blank" href="{{ user.local_path }}/rss"> <a class="icon icon-rss" target="_blank" href="{{ user.local_path }}/rss">
<span class="is-sr-only">RSS feed</span> <span class="is-sr-only">{% trans "RSS feed" %}</span>
</a> </a>
</div> </div>
</div> </div>
@ -69,7 +71,7 @@
{% endfor %} {% endfor %}
{% if not activities %} {% if not activities %}
<div class="block"> <div class="block">
<p>No activities yet!</a> <p>{% trans "No activities yet!" %}</a>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,4 +1,5 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load i18n %}
{% load humanize %} {% load humanize %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
@ -10,35 +11,13 @@
{# user bio #} {# user bio #}
<div class="block"> <div class="block">
<div class="columns"> <div class="columns">
<div class="column is-narrow"> <div class="column is-two-fifths">
<div class="media"> {% include 'user/user_preview.html' with user=user %}
<div class="media-left">
<a href="{{ user.local_path }}">
{% include 'snippets/avatar.html' with user=user large=True %}
</a>
</div>
<div class="media-content">
<p>{% if user.name %}{{ user.name }}{% else %}{{ user.localname }}{% endif %}</p>
<p><a href="{{ user.remote_id }}">{{ user.username }}</a></p>
<p>Joined {{ user.created_date | naturaltime }}</p>
<p>
<a href="{{ user.local_path }}/followers">{{ user.followers.count }} follower{{ user.followers.count | pluralize }}</a>,
<a href="{{ user.local_path }}/following">{{ user.following.count }} following</a>
</p>
</div>
</div>
</div> </div>
<div class="column"> <div class="column box has-background-white-bis content">
{% if user.summary %} {% if user.summary %}
<div class="columns"> {{ user.summary | to_markdown | safe }}
<div class="column is-narrow">
<span class="icon icon-quote-open"></span>
</div>
<div class="column">
<blockquote>{{ user.summary | to_markdown | safe }}</blockquote>
</div>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -48,7 +27,7 @@
{% if is_self and user.follower_requests.all %} {% if is_self and user.follower_requests.all %}
<div class="follow-requests"> <div class="follow-requests">
<h2>Follow Requests</h2> <h2>{% trans "Follow Requests" %}</h2>
{% for requester in user.follower_requests.all %} {% for requester in user.follower_requests.all %}
<div class="row shrink"> <div class="row shrink">
<p> <p>
@ -66,25 +45,25 @@
<ul> <ul>
{% url 'user-feed' user|username as url %} {% url 'user-feed' user|username as url %}
<li{% if url == request.path or url == request.path|add:'/' %} class="is-active"{% endif %}> <li{% if url == request.path or url == request.path|add:'/' %} class="is-active"{% endif %}>
<a href="{{ url }}">Activity</a> <a href="{{ url }}">{% trans "Activity" %}</a>
</li> </li>
{% if is_self or user.goal.exists %} {% if is_self or user.goal.exists %}
{% now 'Y' as year %} {% now 'Y' as year %}
{% url 'user-goal' user|username year as url %} {% url 'user-goal' user|username year as url %}
<li{% if url in request.path %} class="is-active"{% endif %}> <li{% if url in request.path %} class="is-active"{% endif %}>
<a href="{{ url }}">Reading Goal</a> <a href="{{ url }}">{% trans "Reading Goal" %}</a>
</li> </li>
{% endif %} {% endif %}
{% if is_self or user.lists.exists %} {% if is_self or user.lists.exists %}
{% url 'user-lists' user|username as url %} {% url 'user-lists' user|username as url %}
<li{% if url in request.path %} class="is-active"{% endif %}> <li{% if url in request.path %} class="is-active"{% endif %}>
<a href="{{ url }}">Lists</a> <a href="{{ url }}">{% trans "Lists" %}</a>
</li> </li>
{% endif %} {% endif %}
{% if user.shelf_set.exists %} {% if user.shelf_set.exists %}
{% url 'user-shelves' user|username as url %} {% url 'user-shelves' user|username as url %}
<li{% if url in request.path %} class="is-active"{% endif %}> <li{% if url in request.path %} class="is-active"{% endif %}>
<a href="{{ url }}">Shelves</a> <a href="{{ url }}">{% trans "Shelves" %}</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>

View file

@ -0,0 +1,20 @@
{% load i18n %}
{% load humanize %}
<div class="media block">
<div class="media-left">
<a href="{{ user.local_path }}">
{% include 'snippets/avatar.html' with user=user large=True %}
</a>
</div>
<div class="media-content">
<p>{% if user.name %}{{ user.name }}{% else %}{{ user.localname }}{% endif %}</p>
<p><a href="{{ user.remote_id }}">{{ user.username }}</a></p>
<p>{% blocktrans with date=user.created_date|naturaltime %}Joined {{ date }}{% endblocktrans %}</p>
<p>
<a href="{{ user.local_path }}/followers">{{ user.followers.count }} follower{{ user.followers.count | pluralize }}</a>,
<a href="{{ user.local_path }}/following">{{ user.following.count }} following</a>
</p>
</div>
</div>

View file

@ -0,0 +1,4 @@
Book Id Title Sort Character Primary Author Primary Author Role Secondary Author Secondary Author Roles Publication Date Review Rating Comment Private Comment Summary Media Physical Description Weight Height Thickness Length Dimensions Page Count LCCN Acquired Date Started Date Read Barcode BCID Tags Collections Languages Original Languages LC Classification ISBN ISBNs Subjects Dewey Decimal Dewey Wording Other Call Number Copies Source Entry Date From Where OCLC Work id Lending Patron Lending Status Lending Start Lending End
5498194 Marelle 1 Cortázar, Julio Gallimard (1979), Poche 1979 chef d'oeuvre 4.5 Marelle by Julio Cortázar (1979) Broché 590 p.; 7.24 inches 1.28 pounds 7.24 inches 1.26 inches 4.96 inches 7.24 x 4.96 x 1.26 inches 590 [2007-04-16] [2007-05-08] roman, espagnol, expérimental, bohème, philosophie Your library French Spanish PQ7797 .C7145 [2070291340] 2070291340, 9782070291342 Cortâazar, Julio. Rayuela 863 Literature > Spanish And Portuguese > Spanish fiction 1 Amazon.fr [2006-08-09] 57814
5015319 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) 1 Roubaud, Jacques Seuil (1989), Unknown Binding 1989 5 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) by Jacques Roubaud (1989) Broché 411 p.; 7.72 inches 0.88 pounds 7.72 inches 1.02 inches 5.43 inches 7.72 x 5.43 x 1.02 inches 411 Your library English PQ2678 .O77 [2020104725] 2020104725, 9782020104722 Autobiographical fiction|Roubaud, Jacques > Fiction 813 American And Canadian > Fiction > Literature 1 Amazon.com [2006-07-25] 478910
5015399 Le Maître et Marguerite 1 Boulgakov, Mikhaïl Pocket (1994), Poche 1994 Le Maître et Marguerite by Mikhaïl Boulgakov (1994) Broché 579 p.; 7.09 inches 0.66 pounds 7.09 inches 1.18 inches 4.33 inches 7.09 x 4.33 x 1.18 inches 579 Your library French PG3476 .B78 [2266062328] 2266062328, 9782266062329 Allegories|Bulgakov|Good and evil > Fiction|Humanities|Jerusalem > Fiction|Jesus Christ > Fiction|Literature|Mental illness > Fiction|Moscow (Russia) > Fiction|Novel|Pilate, Pontius, 1st cent. > Fiction|Political fiction|Russia > Fiction|Russian fiction|Russian publications (Form Entry)|Soviet Union > History > 1925-1953 > Fiction|literature 891.7342 1917-1945 > 1917-1991 (USSR) > Literature > Literature of other Indo-European languages > Other Languages > Russian > Russian Fiction 1 Amazon.fr [2006-07-25] 10151
1 Book Id Title Sort Character Primary Author Primary Author Role Secondary Author Secondary Author Roles Publication Date Review Rating Comment Private Comment Summary Media Physical Description Weight Height Thickness Length Dimensions Page Count LCCN Acquired Date Started Date Read Barcode BCID Tags Collections Languages Original Languages LC Classification ISBN ISBNs Subjects Dewey Decimal Dewey Wording Other Call Number Copies Source Entry Date From Where OCLC Work id Lending Patron Lending Status Lending Start Lending End
2 5498194 Marelle 1 Cortázar, Julio Gallimard (1979), Poche 1979 chef d'oeuvre 4.5 Marelle by Julio Cortázar (1979) Broché 590 p.; 7.24 inches 1.28 pounds 7.24 inches 1.26 inches 4.96 inches 7.24 x 4.96 x 1.26 inches 590 [2007-04-16] [2007-05-08] roman, espagnol, expérimental, bohème, philosophie Your library French Spanish PQ7797 .C7145 [2070291340] 2070291340, 9782070291342 Cortâazar, Julio. Rayuela 863 Literature > Spanish And Portuguese > Spanish fiction 1 Amazon.fr [2006-08-09] 57814
3 5015319 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) 1 Roubaud, Jacques Seuil (1989), Unknown Binding 1989 5 Le grand incendie de Londres: Récit, avec incises et bifurcations, 1985-1987 (Fiction & Cie) by Jacques Roubaud (1989) Broché 411 p.; 7.72 inches 0.88 pounds 7.72 inches 1.02 inches 5.43 inches 7.72 x 5.43 x 1.02 inches 411 Your library English PQ2678 .O77 [2020104725] 2020104725, 9782020104722 Autobiographical fiction|Roubaud, Jacques > Fiction 813 American And Canadian > Fiction > Literature 1 Amazon.com [2006-07-25] 478910
4 5015399 Le Maître et Marguerite 1 Boulgakov, Mikhaïl Pocket (1994), Poche 1994 Le Maître et Marguerite by Mikhaïl Boulgakov (1994) Broché 579 p.; 7.09 inches 0.66 pounds 7.09 inches 1.18 inches 4.33 inches 7.09 x 4.33 x 1.18 inches 579 Your library French PG3476 .B78 [2266062328] 2266062328, 9782266062329 Allegories|Bulgakov|Good and evil > Fiction|Humanities|Jerusalem > Fiction|Jesus Christ > Fiction|Literature|Mental illness > Fiction|Moscow (Russia) > Fiction|Novel|Pilate, Pontius, 1st cent. > Fiction|Political fiction|Russia > Fiction|Russian fiction|Russian publications (Form Entry)|Soviet Union > History > 1925-1953 > Fiction|literature 891.7342 1917-1945 > 1917-1991 (USSR) > Literature > Literature of other Indo-European languages > Other Languages > Russian > Russian Fiction 1 Amazon.fr [2006-07-25] 10151

View file

@ -7,16 +7,19 @@ from unittest.mock import patch
from django.test import TestCase from django.test import TestCase
import responses import responses
from bookwyrm import goodreads_import, models from bookwyrm import models, importer
from bookwyrm.goodreads_import import GoodreadsImporter
from bookwyrm import importer
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN
class GoodreadsImport(TestCase): class GoodreadsImport(TestCase):
''' importing from goodreads csv ''' ''' importing from goodreads csv '''
def setUp(self): def setUp(self):
self.importer = GoodreadsImporter()
''' use a test csv ''' ''' use a test csv '''
datafile = pathlib.Path(__file__).parent.joinpath( datafile = pathlib.Path(__file__).parent.joinpath(
'data/goodreads.csv') 'data/goodreads.csv')
self.csv = open(datafile, 'r') self.csv = open(datafile, 'r', encoding=self.importer.encoding)
self.user = models.User.objects.create_user( self.user = models.User.objects.create_user(
'mouse', 'mouse@mouse.mouse', 'password', local=True) 'mouse', 'mouse@mouse.mouse', 'password', local=True)
@ -41,7 +44,7 @@ class GoodreadsImport(TestCase):
def test_create_job(self): def test_create_job(self):
''' creates the import job entry and checks csv ''' ''' creates the import job entry and checks csv '''
import_job = goodreads_import.create_job( import_job = self.importer.create_job(
self.user, self.csv, False, 'public') self.user, self.csv, False, 'public')
self.assertEqual(import_job.user, self.user) self.assertEqual(import_job.user, self.user)
self.assertEqual(import_job.include_reviews, False) self.assertEqual(import_job.include_reviews, False)
@ -59,13 +62,13 @@ class GoodreadsImport(TestCase):
def test_create_retry_job(self): def test_create_retry_job(self):
''' trying again with items that didn't import ''' ''' trying again with items that didn't import '''
import_job = goodreads_import.create_job( import_job = self.importer.create_job(
self.user, self.csv, False, 'unlisted') self.user, self.csv, False, 'unlisted')
import_items = models.ImportItem.objects.filter( import_items = models.ImportItem.objects.filter(
job=import_job job=import_job
).all()[:2] ).all()[:2]
retry = goodreads_import.create_retry_job( retry = self.importer.create_retry_job(
self.user, import_job, import_items) self.user, import_job, import_items)
self.assertNotEqual(import_job, retry) self.assertNotEqual(import_job, retry)
self.assertEqual(retry.user, self.user) self.assertEqual(retry.user, self.user)
@ -82,13 +85,13 @@ class GoodreadsImport(TestCase):
def test_start_import(self): def test_start_import(self):
''' begin loading books ''' ''' begin loading books '''
import_job = goodreads_import.create_job( import_job = self.importer.create_job(
self.user, self.csv, False, 'unlisted') self.user, self.csv, False, 'unlisted')
MockTask = namedtuple('Task', ('id')) MockTask = namedtuple('Task', ('id'))
mock_task = MockTask(7) mock_task = MockTask(7)
with patch('bookwyrm.goodreads_import.import_data.delay') as start: with patch('bookwyrm.importer.import_data.delay') as start:
start.return_value = mock_task start.return_value = mock_task
goodreads_import.start_import(import_job) self.importer.start_import(import_job)
import_job.refresh_from_db() import_job.refresh_from_db()
self.assertEqual(import_job.task_id, '7') self.assertEqual(import_job.task_id, '7')
@ -96,7 +99,7 @@ class GoodreadsImport(TestCase):
@responses.activate @responses.activate
def test_import_data(self): def test_import_data(self):
''' resolve entry ''' ''' resolve entry '''
import_job = goodreads_import.create_job( import_job = self.importer.create_job(
self.user, self.csv, False, 'unlisted') self.user, self.csv, False, 'unlisted')
book = models.Edition.objects.create(title='Test Book') book = models.Edition.objects.create(title='Test Book')
@ -104,8 +107,8 @@ class GoodreadsImport(TestCase):
'bookwyrm.models.import_job.ImportItem.get_book_from_isbn' 'bookwyrm.models.import_job.ImportItem.get_book_from_isbn'
) as resolve: ) as resolve:
resolve.return_value = book resolve.return_value = book
with patch('bookwyrm.goodreads_import.handle_imported_book'): with patch('bookwyrm.importer.handle_imported_book'):
goodreads_import.import_data(import_job.id) importer.import_data(self.importer.service, import_job.id)
import_item = models.ImportItem.objects.get(job=import_job, index=0) import_item = models.ImportItem.objects.get(job=import_job, index=0)
self.assertEqual(import_item.book.id, book.id) self.assertEqual(import_item.book.id, book.id)
@ -120,13 +123,14 @@ class GoodreadsImport(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv') datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv')
csv_file = open(datafile, 'r') csv_file = open(datafile, 'r')
for index, entry in enumerate(list(csv.DictReader(csv_file))): for index, entry in enumerate(list(csv.DictReader(csv_file))):
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create( import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=index, data=entry, book=self.book) job_id=import_job.id, index=index, data=entry, book=self.book)
break break
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
goodreads_import.handle_imported_book( importer.handle_imported_book(
self.user, import_item, False, 'public') self.importer.service, self.user, import_item, False, 'public')
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -153,13 +157,14 @@ class GoodreadsImport(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv') datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv')
csv_file = open(datafile, 'r') csv_file = open(datafile, 'r')
for index, entry in enumerate(list(csv.DictReader(csv_file))): for index, entry in enumerate(list(csv.DictReader(csv_file))):
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create( import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=index, data=entry, book=self.book) job_id=import_job.id, index=index, data=entry, book=self.book)
break break
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
goodreads_import.handle_imported_book( importer.handle_imported_book(
self.user, import_item, False, 'public') self.importer.service, self.user, import_item, False, 'public')
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -182,15 +187,16 @@ class GoodreadsImport(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv') datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv')
csv_file = open(datafile, 'r') csv_file = open(datafile, 'r')
for index, entry in enumerate(list(csv.DictReader(csv_file))): for index, entry in enumerate(list(csv.DictReader(csv_file))):
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create( import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=index, data=entry, book=self.book) job_id=import_job.id, index=index, data=entry, book=self.book)
break break
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
goodreads_import.handle_imported_book( importer.handle_imported_book(
self.user, import_item, False, 'public') self.importer.service, self.user, import_item, False, 'public')
goodreads_import.handle_imported_book( importer.handle_imported_book(
self.user, import_item, False, 'public') self.importer.service, self.user, import_item, False, 'public')
shelf.refresh_from_db() shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book) self.assertEqual(shelf.books.first(), self.book)
@ -212,12 +218,13 @@ class GoodreadsImport(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv') datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv')
csv_file = open(datafile, 'r') csv_file = open(datafile, 'r')
entry = list(csv.DictReader(csv_file))[2] entry = list(csv.DictReader(csv_file))[2]
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create( import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=0, data=entry, book=self.book) job_id=import_job.id, index=0, data=entry, book=self.book)
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
goodreads_import.handle_imported_book( importer.handle_imported_book(
self.user, import_item, True, 'unlisted') self.importer.service, self.user, import_item, True, 'unlisted')
review = models.Review.objects.get(book=self.book, user=self.user) review = models.Review.objects.get(book=self.book, user=self.user)
self.assertEqual(review.content, 'mixed feelings') self.assertEqual(review.content, 'mixed feelings')
self.assertEqual(review.rating, 2) self.assertEqual(review.rating, 2)
@ -233,12 +240,13 @@ class GoodreadsImport(TestCase):
datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv') datafile = pathlib.Path(__file__).parent.joinpath('data/goodreads.csv')
csv_file = open(datafile, 'r') csv_file = open(datafile, 'r')
entry = list(csv.DictReader(csv_file))[2] entry = list(csv.DictReader(csv_file))[2]
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create( import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=0, data=entry, book=self.book) job_id=import_job.id, index=0, data=entry, book=self.book)
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
goodreads_import.handle_imported_book( importer.handle_imported_book(
self.user, import_item, False, 'unlisted') self.importer.service, self.user, import_item, False, 'unlisted')
self.assertFalse(models.Review.objects.filter( self.assertFalse(models.Review.objects.filter(
book=self.book, user=self.user book=self.book, user=self.user
).exists()) ).exists())

View file

@ -0,0 +1,240 @@
''' testing import '''
from collections import namedtuple
import csv
import pathlib
from unittest.mock import patch
from django.test import TestCase
import responses
from bookwyrm import models, importer
from bookwyrm.librarything_import import LibrarythingImporter
from bookwyrm.settings import DOMAIN
class LibrarythingImport(TestCase):
''' importing from librarything tsv '''
def setUp(self):
self.importer = LibrarythingImporter()
''' use a test tsv '''
datafile = pathlib.Path(__file__).parent.joinpath(
'data/librarything.tsv')
# Librarything generates latin encoded exports...
self.csv = open(datafile, 'r', encoding=self.importer.encoding)
self.user = models.User.objects.create_user(
'mmai', 'mmai@mmai.mmai', 'password', local=True)
models.Connector.objects.create(
identifier=DOMAIN,
name='Local',
local=True,
connector_file='self_connector',
base_url='https://%s' % DOMAIN,
books_url='https://%s/book' % DOMAIN,
covers_url='https://%s/images/covers' % DOMAIN,
search_url='https://%s/search?q=' % DOMAIN,
priority=1,
)
work = models.Work.objects.create(title='Test Work')
self.book = models.Edition.objects.create(
title='Example Edition',
remote_id='https://example.com/book/1',
parent_work=work
)
def test_create_job(self):
''' creates the import job entry and checks csv '''
import_job = self.importer.create_job(
self.user, self.csv, False, 'public')
self.assertEqual(import_job.user, self.user)
self.assertEqual(import_job.include_reviews, False)
self.assertEqual(import_job.privacy, 'public')
import_items = models.ImportItem.objects.filter(job=import_job).all()
self.assertEqual(len(import_items), 3)
self.assertEqual(import_items[0].index, 0)
self.assertEqual(import_items[0].data['Book Id'], '5498194')
self.assertEqual(import_items[1].index, 1)
self.assertEqual(import_items[1].data['Book Id'], '5015319')
self.assertEqual(import_items[2].index, 2)
self.assertEqual(import_items[2].data['Book Id'], '5015399')
def test_create_retry_job(self):
''' trying again with items that didn't import '''
import_job = self.importer.create_job(
self.user, self.csv, False, 'unlisted')
import_items = models.ImportItem.objects.filter(
job=import_job
).all()[:2]
retry = self.importer.create_retry_job(
self.user, import_job, import_items)
self.assertNotEqual(import_job, retry)
self.assertEqual(retry.user, self.user)
self.assertEqual(retry.include_reviews, False)
self.assertEqual(retry.privacy, 'unlisted')
retry_items = models.ImportItem.objects.filter(job=retry).all()
self.assertEqual(len(retry_items), 2)
self.assertEqual(retry_items[0].index, 0)
self.assertEqual(import_items[0].data['Book Id'], '5498194')
self.assertEqual(retry_items[1].index, 1)
self.assertEqual(retry_items[1].data['Book Id'], '5015319')
@responses.activate
def test_import_data(self):
''' resolve entry '''
import_job = self.importer.create_job(
self.user, self.csv, False, 'unlisted')
book = models.Edition.objects.create(title='Test Book')
with patch(
'bookwyrm.models.import_job.ImportItem.get_book_from_isbn'
) as resolve:
resolve.return_value = book
with patch('bookwyrm.importer.handle_imported_book'):
importer.import_data(self.importer.service, import_job.id)
import_item = models.ImportItem.objects.get(job=import_job, index=0)
self.assertEqual(import_item.book.id, book.id)
def test_handle_imported_book(self):
''' librarything import added a book, this adds related connections '''
shelf = self.user.shelf_set.filter(identifier='read').first()
self.assertIsNone(shelf.books.first())
import_job = models.ImportJob.objects.create(user=self.user)
datafile = pathlib.Path(__file__).parent.joinpath('data/librarything.tsv')
csv_file = open(datafile, 'r', encoding=self.importer.encoding)
for index, entry in enumerate(list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))):
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=index, data=entry, book=self.book)
break
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
importer.handle_imported_book(
self.importer.service, self.user, import_item, False, 'public')
shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book)
readthrough = models.ReadThrough.objects.get(user=self.user)
self.assertEqual(readthrough.book, self.book)
# I can't remember how to create dates and I don't want to look it up.
self.assertEqual(readthrough.start_date.year, 2007)
self.assertEqual(readthrough.start_date.month, 4)
self.assertEqual(readthrough.start_date.day, 16)
self.assertEqual(readthrough.finish_date.year, 2007)
self.assertEqual(readthrough.finish_date.month, 5)
self.assertEqual(readthrough.finish_date.day, 8)
def test_handle_imported_book_already_shelved(self):
''' librarything import added a book, this adds related connections '''
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
shelf = self.user.shelf_set.filter(identifier='to-read').first()
models.ShelfBook.objects.create(
shelf=shelf, user=self.user, book=self.book)
import_job = models.ImportJob.objects.create(user=self.user)
datafile = pathlib.Path(__file__).parent.joinpath('data/librarything.tsv')
csv_file = open(datafile, 'r', encoding=self.importer.encoding)
for index, entry in enumerate(list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))):
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=index, data=entry, book=self.book)
break
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
importer.handle_imported_book(
self.importer.service, self.user, import_item, False, 'public')
shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book)
self.assertIsNone(
self.user.shelf_set.get(identifier='read').books.first())
readthrough = models.ReadThrough.objects.get(user=self.user)
self.assertEqual(readthrough.book, self.book)
self.assertEqual(readthrough.start_date.year, 2007)
self.assertEqual(readthrough.start_date.month, 4)
self.assertEqual(readthrough.start_date.day, 16)
self.assertEqual(readthrough.finish_date.year, 2007)
self.assertEqual(readthrough.finish_date.month, 5)
self.assertEqual(readthrough.finish_date.day, 8)
def test_handle_import_twice(self):
''' re-importing books '''
shelf = self.user.shelf_set.filter(identifier='read').first()
import_job = models.ImportJob.objects.create(user=self.user)
datafile = pathlib.Path(__file__).parent.joinpath('data/librarything.tsv')
csv_file = open(datafile, 'r', encoding=self.importer.encoding)
for index, entry in enumerate(list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))):
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=index, data=entry, book=self.book)
break
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
importer.handle_imported_book(
self.importer.service, self.user, import_item, False, 'public')
importer.handle_imported_book(
self.importer.service, self.user, import_item, False, 'public')
shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book)
readthrough = models.ReadThrough.objects.get(user=self.user)
self.assertEqual(readthrough.book, self.book)
# I can't remember how to create dates and I don't want to look it up.
self.assertEqual(readthrough.start_date.year, 2007)
self.assertEqual(readthrough.start_date.month, 4)
self.assertEqual(readthrough.start_date.day, 16)
self.assertEqual(readthrough.finish_date.year, 2007)
self.assertEqual(readthrough.finish_date.month, 5)
self.assertEqual(readthrough.finish_date.day, 8)
def test_handle_imported_book_review(self):
''' librarything review import '''
import_job = models.ImportJob.objects.create(user=self.user)
datafile = pathlib.Path(__file__).parent.joinpath('data/librarything.tsv')
csv_file = open(datafile, 'r', encoding=self.importer.encoding)
entry = list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))[0]
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=0, data=entry, book=self.book)
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
importer.handle_imported_book(
self.importer.service, self.user, import_item, True, 'unlisted')
review = models.Review.objects.get(book=self.book, user=self.user)
self.assertEqual(review.content, 'chef d\'oeuvre')
self.assertEqual(review.rating, 5)
self.assertEqual(review.published_date.year, 2007)
self.assertEqual(review.published_date.month, 5)
self.assertEqual(review.published_date.day, 8)
self.assertEqual(review.privacy, 'unlisted')
def test_handle_imported_book_reviews_disabled(self):
''' librarything review import '''
import_job = models.ImportJob.objects.create(user=self.user)
datafile = pathlib.Path(__file__).parent.joinpath('data/librarything.tsv')
csv_file = open(datafile, 'r', encoding=self.importer.encoding)
entry = list(csv.DictReader(csv_file, delimiter=self.importer.delimiter))[2]
entry = self.importer.parse_fields(entry)
import_item = models.ImportItem.objects.create(
job_id=import_job.id, index=0, data=entry, book=self.book)
with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'):
importer.handle_imported_book(
self.importer.service, self.user, import_item, False, 'unlisted')
self.assertFalse(models.Review.objects.filter(
book=self.book, user=self.user
).exists())

View file

@ -9,7 +9,7 @@ from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import View from django.views import View
from bookwyrm import forms, goodreads_import, models from bookwyrm import forms, goodreads_import, librarything_import, models
from bookwyrm.tasks import app from bookwyrm.tasks import app
# pylint: disable= no-self-use # pylint: disable= no-self-use
@ -31,18 +31,29 @@ class Import(View):
if form.is_valid(): if form.is_valid():
include_reviews = request.POST.get('include_reviews') == 'on' include_reviews = request.POST.get('include_reviews') == 'on'
privacy = request.POST.get('privacy') privacy = request.POST.get('privacy')
source = request.POST.get('source')
importer = None
if source == 'LibraryThing':
importer = librarything_import.LibrarythingImporter()
else:
# Default : GoodReads
importer = goodreads_import.GoodreadsImporter()
try: try:
job = goodreads_import.create_job( job = importer.create_job(
request.user, request.user,
TextIOWrapper( TextIOWrapper(
request.FILES['csv_file'], request.FILES['csv_file'],
encoding=request.encoding), encoding=importer.encoding),
include_reviews, include_reviews,
privacy, privacy,
) )
except (UnicodeDecodeError, ValueError): except (UnicodeDecodeError, ValueError):
return HttpResponseBadRequest('Not a valid csv file') return HttpResponseBadRequest('Not a valid csv file')
goodreads_import.start_import(job)
importer.start_import(job)
return redirect('/import/%d' % job.id) return redirect('/import/%d' % job.id)
return HttpResponseBadRequest() return HttpResponseBadRequest()

Some files were not shown because too many files have changed in this diff Show more