mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-15 20:56:34 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
7439adb8e6
24 changed files with 287 additions and 178 deletions
|
@ -2,7 +2,7 @@
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models, settings
|
||||||
from bookwyrm.tasks import app
|
from bookwyrm.tasks import app
|
||||||
from bookwyrm.settings import DOMAIN
|
from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
@ -59,6 +59,8 @@ def format_email(email_name, data):
|
||||||
@app.task
|
@app.task
|
||||||
def send_email(recipient, subject, html_content, text_content):
|
def send_email(recipient, subject, html_content, text_content):
|
||||||
""" use a task to send the email """
|
""" use a task to send the email """
|
||||||
email = EmailMultiAlternatives(subject, text_content, None, [recipient])
|
email = EmailMultiAlternatives(
|
||||||
|
subject, text_content, settings.DEFAULT_FROM_EMAIL, [recipient]
|
||||||
|
)
|
||||||
email.attach_alternative(html_content, "text/html")
|
email.attach_alternative(html_content, "text/html")
|
||||||
email.send()
|
email.send()
|
||||||
|
|
|
@ -83,7 +83,7 @@ class InviteRequest(BookWyrmModel):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
""" don't create a request for a registered email """
|
""" don't create a request for a registered email """
|
||||||
if User.objects.filter(email=self.email).exists():
|
if not self.id and User.objects.filter(email=self.email).exists():
|
||||||
raise IntegrityError()
|
raise IntegrityError()
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ EMAIL_PORT = env("EMAIL_PORT", 587)
|
||||||
EMAIL_HOST_USER = env("EMAIL_HOST_USER")
|
EMAIL_HOST_USER = env("EMAIL_HOST_USER")
|
||||||
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD")
|
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD")
|
||||||
EMAIL_USE_TLS = env("EMAIL_USE_TLS", True)
|
EMAIL_USE_TLS = env("EMAIL_USE_TLS", True)
|
||||||
|
DEFAULT_FROM_EMAIL = "admin@{:s}".format(env("DOMAIN"))
|
||||||
|
|
||||||
# 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__)))
|
||||||
|
|
|
@ -76,24 +76,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<p>
|
{% include 'book/publisher_info.html' with book=book %}
|
||||||
{% if book.physical_format and not book.pages %}
|
|
||||||
{{ book.physical_format | title }}
|
|
||||||
{% elif book.physical_format and book.pages %}
|
|
||||||
{% blocktrans with format=book.physical_format|title pages=book.pages %}{{ format }}, {{ pages }} pages{% endblocktrans %}
|
|
||||||
{% elif book.pages %}
|
|
||||||
{% blocktrans with pages=book.pages %}{{ pages }} pages{% endblocktrans %}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{% if book.published_date and book.publishers %}
|
|
||||||
{% blocktrans with date=book.published_date|date:'M jS Y' publisher=book.publishers|join:', ' %}Published {{ date }} by {{ publisher }}.{% endblocktrans %}
|
|
||||||
{% elif book.published_date %}
|
|
||||||
{% blocktrans with date=book.published_date|date:'M jS Y' %}Published {{ date }}{% endblocktrans %}
|
|
||||||
{% elif book.publishers %}
|
|
||||||
{% blocktrans with publisher=book.publishers|join:', ' %}Published by {{ publisher }}.{% endblocktrans %}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{% if book.openlibrary_key %}
|
{% if book.openlibrary_key %}
|
||||||
<p><a href="https://openlibrary.org/books/{{ book.openlibrary_key }}" target="_blank" rel="noopener">{% trans "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>
|
||||||
|
|
6
bookwyrm/templates/book/edition_filters.html
Normal file
6
bookwyrm/templates/book/edition_filters.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{% extends 'snippets/filters_panel/filters_panel.html' %}
|
||||||
|
|
||||||
|
{% block filter_fields %}
|
||||||
|
{% include 'book/language_filter.html' %}
|
||||||
|
{% include 'book/format_filter.html' %}
|
||||||
|
{% endblock %}
|
36
bookwyrm/templates/book/editions.html
Normal file
36
bookwyrm/templates/book/editions.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load bookwyrm_tags %}
|
||||||
|
|
||||||
|
{% block title %}{% blocktrans with book_title=work.title %}Editions of {{ book_title }}{% endblocktrans %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="block">
|
||||||
|
<h1 class="title">{% blocktrans with work_path=work.local_path work_title=work.title %}Editions of <a href="{{ work_path }}">"{{ work_title }}"</a>{% endblocktrans %}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include 'book/edition_filters.html' %}
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
{% for book in editions %}
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-2">
|
||||||
|
<a href="/book/{{ book.id }}">
|
||||||
|
{% include 'snippets/book_cover.html' with book=book size="medium" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="column is-7">
|
||||||
|
<h2 class="title is-5">
|
||||||
|
<a href="/book/{{ book.id }}" class="has-text-black">
|
||||||
|
{{ book.title }}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
{% include 'book/publisher_info.html' with book=book %}
|
||||||
|
</div>
|
||||||
|
<div class="column is-3">
|
||||||
|
{% include 'snippets/shelve_button/shelve_button.html' with book=book switch_mode=True %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
16
bookwyrm/templates/book/format_filter.html
Normal file
16
bookwyrm/templates/book/format_filter.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends 'snippets/filters_panel/filter_field.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block filter %}
|
||||||
|
<label class="label is-block" for="id_format">{% trans "Format:" %}</label>
|
||||||
|
<div class="select">
|
||||||
|
<select id="id_format" name="format">
|
||||||
|
<option value="">{% trans "Any" %}</option>
|
||||||
|
{% for format in formats %}{% if format %}
|
||||||
|
<option value="{{ format }}" {% if request.GET.format == format %}selected{% endif %}>
|
||||||
|
{{ format|title }}
|
||||||
|
</option>
|
||||||
|
{% endif %}{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
16
bookwyrm/templates/book/language_filter.html
Normal file
16
bookwyrm/templates/book/language_filter.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends 'snippets/filters_panel/filter_field.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block filter %}
|
||||||
|
<label class="label is-block" for="id_language">{% trans "Language:" %}</label>
|
||||||
|
<div class="select">
|
||||||
|
<select id="id_language" name="language">
|
||||||
|
<option value="">{% trans "Any" %}</option>
|
||||||
|
{% for language in languages %}
|
||||||
|
<option value="{{ language }}" {% if request.GET.language == language %}selected{% endif %}>
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
24
bookwyrm/templates/book/publisher_info.html
Normal file
24
bookwyrm/templates/book/publisher_info.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<p>
|
||||||
|
{% if book.physical_format and not book.pages %}
|
||||||
|
{{ book.physical_format | title }}
|
||||||
|
{% elif book.physical_format and book.pages %}
|
||||||
|
{% blocktrans with format=book.physical_format|title pages=book.pages %}{{ format }}, {{ pages }} pages{% endblocktrans %}
|
||||||
|
{% elif book.pages %}
|
||||||
|
{% blocktrans with pages=book.pages %}{{ pages }} pages{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{% if book.languages %}
|
||||||
|
<p>
|
||||||
|
{% blocktrans with languages=book.languages|join:", " %}{{ languages }} language{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<p>
|
||||||
|
{% if book.published_date and book.publishers %}
|
||||||
|
{% blocktrans with date=book.published_date|date:'M jS Y' publisher=book.publishers|join:', ' %}Published {{ date }} by {{ publisher }}.{% endblocktrans %}
|
||||||
|
{% elif book.published_date %}
|
||||||
|
{% blocktrans with date=book.published_date|date:'M jS Y' %}Published {{ date }}{% endblocktrans %}
|
||||||
|
{% elif book.publishers %}
|
||||||
|
{% blocktrans with publisher=book.publishers|join:', ' %}Published by {{ publisher }}.{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
14
bookwyrm/templates/directory/community_filter.html
Normal file
14
bookwyrm/templates/directory/community_filter.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends 'snippets/filters_panel/filter_field.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block filter %}
|
||||||
|
<legend class="label">{% trans "Community" %}</legend>
|
||||||
|
<label class="is-block">
|
||||||
|
<input type="radio" class="radio" name="scope" value="local" {% if request.GET.scope == "local" %}checked{% endif %}>
|
||||||
|
{% trans "Local users" %}
|
||||||
|
</label>
|
||||||
|
<label class="is-block">
|
||||||
|
<input type="radio" class="radio" name="scope" value="federated" {% if not request.GET.sort or request.GET.scope == "federated" %}checked{% endif %}>
|
||||||
|
{% trans "Federated community" %}
|
||||||
|
</label>
|
||||||
|
{% endblock %}
|
|
@ -36,64 +36,7 @@
|
||||||
</div></div>
|
</div></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="notification content">
|
{% include 'directory/filters.html' %}
|
||||||
<h2 class="columns is-mobile mb-0">
|
|
||||||
<span class="column pb-0">Filters</span>
|
|
||||||
|
|
||||||
<span class="column is-narrow pb-0">
|
|
||||||
{% trans "Show filters" as text %}
|
|
||||||
{% include 'snippets/toggle/open_button.html' with text=text controls_text="filters" icon="arrow-down" class="is-small" focus="filters" %}
|
|
||||||
{% trans "Hide filters" as text %}
|
|
||||||
{% include 'snippets/toggle/close_button.html' with text=text controls_text="filters" icon="x" class="is-small" %}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<form class="hidden mt-3" id="filters" method-"get" action="{% url 'directory' %}" tabindex="0">
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-flex">
|
|
||||||
<div class="box is-flex-grow-1">
|
|
||||||
<legend class="label">{% trans "User type" %}</legend>
|
|
||||||
<label class="is-block">
|
|
||||||
<input type="radio" class="radio" name="software" value="bookwyrm" {% if not request.GET.sort or request.GET.software == 'bookwyrm' %}checked{% endif %}>
|
|
||||||
{% trans "BookWyrm users" %}
|
|
||||||
</label>
|
|
||||||
<label class="is-block">
|
|
||||||
<input type="radio" class="radio" name="software" value="all" {% if request.GET.software == 'all' %}checked{% endif %}>
|
|
||||||
{% trans "All known users" %}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column is-flex">
|
|
||||||
<div class="box is-flex-grow-1">
|
|
||||||
<legend class="label">{% trans "Community" %}</legend>
|
|
||||||
<label class="is-block">
|
|
||||||
<input type="radio" class="radio" name="scope" value="local" {% if request.GET.scope == "local" %}checked{% endif %}>
|
|
||||||
{% trans "Local users" %}
|
|
||||||
</label>
|
|
||||||
<label class="is-block">
|
|
||||||
<input type="radio" class="radio" name="scope" value="federated" {% if not request.GET.sort or request.GET.scope == "federated" %}checked{% endif %}>
|
|
||||||
{% trans "Federated community" %}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column is-flex">
|
|
||||||
<div class="box is-flex-grow-1">
|
|
||||||
<label class="label" for="id_sort">{% trans "Order by" %}</label>
|
|
||||||
<div class="select">
|
|
||||||
<select name="sort" id="id_sort">
|
|
||||||
<option value="suggested" {% if not request.GET.sort or request.GET.sort == "suggested" %}checked{% endif %}>{% trans "Suggested" %}</option>
|
|
||||||
<option value="recent" {% if request.GET.sort == "suggested" %}checked{% endif %}>{% trans "Recently active" %}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="button is-primary">{% trans "Apply filters" %}</button>
|
|
||||||
</form>
|
|
||||||
{% if request.GET %}
|
|
||||||
<a class="help" href="{% url 'directory' %}">{% trans "Clear filters" %}</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="columns is-multiline">
|
<div class="columns is-multiline">
|
||||||
{% for user in users %}
|
{% for user in users %}
|
7
bookwyrm/templates/directory/filters.html
Normal file
7
bookwyrm/templates/directory/filters.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends 'snippets/filters_panel/filters_panel.html' %}
|
||||||
|
|
||||||
|
{% block filter_fields %}
|
||||||
|
{% include 'directory/user_type_filter.html' %}
|
||||||
|
{% include 'directory/community_filter.html' %}
|
||||||
|
{% include 'directory/sort_filter.html' %}
|
||||||
|
{% endblock %}
|
12
bookwyrm/templates/directory/sort_filter.html
Normal file
12
bookwyrm/templates/directory/sort_filter.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends 'snippets/filters_panel/filter_field.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block filter %}
|
||||||
|
<label class="label" for="id_sort">{% trans "Order by" %}</label>
|
||||||
|
<div class="select">
|
||||||
|
<select name="sort" id="id_sort">
|
||||||
|
<option value="suggested" {% if not request.GET.sort or request.GET.sort == "suggested" %}checked{% endif %}>{% trans "Suggested" %}</option>
|
||||||
|
<option value="recent" {% if request.GET.sort == "suggested" %}checked{% endif %}>{% trans "Recently active" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
14
bookwyrm/templates/directory/user_type_filter.html
Normal file
14
bookwyrm/templates/directory/user_type_filter.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends 'snippets/filters_panel/filter_field.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block filter %}
|
||||||
|
<legend class="label">{% trans "User type" %}</legend>
|
||||||
|
<label class="is-block">
|
||||||
|
<input type="radio" class="radio" name="software" value="bookwyrm" {% if not request.GET.sort or request.GET.software == 'bookwyrm' %}checked{% endif %}>
|
||||||
|
{% trans "BookWyrm users" %}
|
||||||
|
</label>
|
||||||
|
<label class="is-block">
|
||||||
|
<input type="radio" class="radio" name="software" value="all" {% if request.GET.software == 'all' %}checked{% endif %}>
|
||||||
|
{% trans "All known users" %}
|
||||||
|
</label>
|
||||||
|
{% endblock %}
|
|
@ -1,14 +0,0 @@
|
||||||
{% extends 'layout.html' %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load bookwyrm_tags %}
|
|
||||||
|
|
||||||
{% block title %}{% blocktrans with book_title=work.title %}Editions of {{ book_title }}{% endblocktrans %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="block">
|
|
||||||
<h1 class="title">{% blocktrans with work_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 %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -107,7 +107,7 @@
|
||||||
{% trans 'Settings' %}
|
{% trans 'Settings' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% if perms.bookwyrm.create_invites or perms.bookwyrm.edit_instance_settings%}
|
{% if perms.bookwyrm.create_invites or perms.moderate_users %}
|
||||||
<li class="navbar-divider" role="presentation"></li>
|
<li class="navbar-divider" role="presentation"></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.bookwyrm.create_invites %}
|
{% if perms.bookwyrm.create_invites %}
|
||||||
|
@ -117,9 +117,9 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.bookwyrm.edit_instance_settings %}
|
{% if perms.bookwyrm.moderate_users %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'settings-reports' %}" class="navbar-item">
|
<a href="{% url 'settings-users' %}" class="navbar-item">
|
||||||
{% trans 'Admin' %}
|
{% trans 'Admin' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="column is-flex">
|
||||||
|
<div class="box is-flex-grow-1">
|
||||||
|
{% block filter %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
25
bookwyrm/templates/snippets/filters_panel/filters_panel.html
Normal file
25
bookwyrm/templates/snippets/filters_panel/filters_panel.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{% load i18n %}
|
||||||
|
<div class="notification content">
|
||||||
|
<h2 class="columns is-mobile mb-0">
|
||||||
|
<span class="column pb-0">Filters</span>
|
||||||
|
|
||||||
|
<span class="column is-narrow pb-0">
|
||||||
|
{% trans "Show filters" as text %}
|
||||||
|
{% include 'snippets/toggle/open_button.html' with text=text controls_text="filters" icon="arrow-down" class="is-small" focus="filters" %}
|
||||||
|
{% trans "Hide filters" as text %}
|
||||||
|
{% include 'snippets/toggle/close_button.html' with text=text controls_text="filters" icon="x" class="is-small" %}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<form class="hidden mt-3" id="filters" method-"get" action="{{ request.path }}" tabindex="0">
|
||||||
|
<div class="columns">
|
||||||
|
{% block filter_fields %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
<button class="button is-primary">{% trans "Apply filters" %}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if request.GET %}
|
||||||
|
<a class="help" href="{{ request.path }}">{% trans "Clear filters" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
|
@ -2,7 +2,7 @@
|
||||||
<nav class="pagination" aria-label="pagination">
|
<nav class="pagination" 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 }}?{% for k, v in request.GET.items %}{% if k != 'page' %}{{ k }}={{ v }}&{% endif %}{% endfor %}page={{ page.previous_page_number }}{{ anchor }}">
|
||||||
<span class="icon icon-arrow-left"></span>
|
<span class="icon icon-arrow-left"></span>
|
||||||
{% trans "Previous" %}
|
{% trans "Previous" %}
|
||||||
</a>
|
</a>
|
||||||
|
@ -11,7 +11,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 }}?{% for k, v in request.GET.items %}{% if k != 'page' %}{{ k }}={{ v }}&{% endif %}{% endfor %}page={{ page.next_page_number }}{{ anchor }}">
|
||||||
{% trans "Next" %}
|
{% trans "Next" %}
|
||||||
<span class="icon icon-arrow-right"></span>
|
<span class="icon icon-arrow-right"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
{% load humanize %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load bookwyrm_tags %}
|
|
||||||
{% if books|length > 0 %}
|
|
||||||
<div class="scroll-x">
|
|
||||||
<table class="table is-striped is-fullwidth">
|
|
||||||
|
|
||||||
<tr class="book-preview">
|
|
||||||
<th>{% trans "Cover" %}</th>
|
|
||||||
<th>{% trans "Title" %}</th>
|
|
||||||
<th>{% trans "Author" %}</th>
|
|
||||||
<th>{% trans "Published" %}</th>
|
|
||||||
<th>{% trans "Shelved" %}</th>
|
|
||||||
<th>{% trans "Started" %}</th>
|
|
||||||
<th>{% trans "Finished" %}</th>
|
|
||||||
<th>{% trans "External links" %}</th>{% if ratings %}
|
|
||||||
<th>{% trans "Rating" %}</th>{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% for book in books %}
|
|
||||||
<tr class="book-preview">
|
|
||||||
<td>
|
|
||||||
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="{{ book.local_path }}">{{ book.title }}</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ book.authors.first.name }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if book.first_published_date %}{{ book.first_published_date }}{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ book.created_date | naturalday }}
|
|
||||||
</td>
|
|
||||||
{% latest_read_through book user as read_through %}
|
|
||||||
<td>
|
|
||||||
{{ read_through.start_date | naturalday |default_if_none:""}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ read_through.finish_date | naturalday |default_if_none:""}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a href="https://openlibrary.org/book/{{ book.openlibrary_key }}" target="_blank">{% trans "OpenLibrary" %}</a>
|
|
||||||
</td>
|
|
||||||
{% if ratings %}
|
|
||||||
<td>
|
|
||||||
{% include 'snippets/stars.html' with rating=ratings|dict_key:book.id %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
{% if shelf.user == request.user %}
|
|
||||||
<td>
|
|
||||||
{% include 'snippets/shelf_selector.html' with current=shelf %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<p>{% trans "This shelf is empty." %}</p>
|
|
||||||
{% if shelf.editable %}
|
|
||||||
<form name="delete-shelf" action="/delete-shelf/{{ shelf.id }}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="user" value="{{ request.user.id }}">
|
|
||||||
<button class="button is-danger is-light" type="submit">
|
|
||||||
{% trans "Delete shelf" %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends 'user/user_layout.html' %}
|
{% extends 'user/user_layout.html' %}
|
||||||
{% load bookwyrm_tags %}
|
{% load bookwyrm_tags %}
|
||||||
|
{% load humanize %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
|
@ -63,7 +64,76 @@
|
||||||
|
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<div>
|
<div>
|
||||||
{% include 'snippets/shelf.html' with shelf=shelf books=books ratings=ratings %}
|
{% if books|length > 0 %}
|
||||||
|
<div class="scroll-x">
|
||||||
|
<table class="table is-striped is-fullwidth">
|
||||||
|
|
||||||
|
<tr class="book-preview">
|
||||||
|
<th>{% trans "Cover" %}</th>
|
||||||
|
<th>{% trans "Title" %}</th>
|
||||||
|
<th>{% trans "Author" %}</th>
|
||||||
|
<th>{% trans "Shelved" %}</th>
|
||||||
|
<th>{% trans "Started" %}</th>
|
||||||
|
<th>{% trans "Finished" %}</th>
|
||||||
|
{% if ratings %}<th>{% trans "Rating" %}</th>{% endif %}
|
||||||
|
{% if shelf.user == request.user %}
|
||||||
|
<th aria-hidden="true"></th>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% for book in books %}
|
||||||
|
{% with book=book.book %}
|
||||||
|
<tr class="book-preview">
|
||||||
|
<td>
|
||||||
|
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=book size="small" %}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ book.local_path }}">{{ book.title }}</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% include 'snippets/authors.html' %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ book.created_date | naturalday }}
|
||||||
|
</td>
|
||||||
|
{% latest_read_through book user as read_through %}
|
||||||
|
<td>
|
||||||
|
{{ read_through.start_date | naturalday |default_if_none:""}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ read_through.finish_date | naturalday |default_if_none:""}}
|
||||||
|
</td>
|
||||||
|
{% if ratings %}
|
||||||
|
<td>
|
||||||
|
{% include 'snippets/stars.html' with rating=ratings|dict_key:book.id %}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
{% if shelf.user == request.user %}
|
||||||
|
<td>
|
||||||
|
{% include 'snippets/shelf_selector.html' with current=shelf %}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p>{% trans "This shelf is empty." %}</p>
|
||||||
|
{% if shelf.editable %}
|
||||||
|
<form name="delete-shelf" action="/delete-shelf/{{ shelf.id }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="user" value="{{ request.user.id }}">
|
||||||
|
<button class="button is-danger is-light" type="submit">
|
||||||
|
{% trans "Delete shelf" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{% include 'snippets/pagination.html' with page=books path=request.path %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -252,12 +252,25 @@ class Editions(View):
|
||||||
|
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return ActivitypubResponse(work.to_edition_list(**request.GET))
|
return ActivitypubResponse(work.to_edition_list(**request.GET))
|
||||||
|
filters = {}
|
||||||
|
|
||||||
|
if request.GET.get("language"):
|
||||||
|
filters["languages__contains"] = [request.GET.get("language")]
|
||||||
|
if request.GET.get("format"):
|
||||||
|
filters["physical_format__iexact"] = request.GET.get("format")
|
||||||
|
|
||||||
|
editions = work.editions.order_by("-edition_rank").all()
|
||||||
|
languages = set(sum([e.languages for e in editions], []))
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"editions": work.editions.order_by("-edition_rank").all(),
|
"editions": editions.filter(**filters).all(),
|
||||||
"work": work,
|
"work": work,
|
||||||
|
"languages": languages,
|
||||||
|
"formats": set(
|
||||||
|
e.physical_format.lower() for e in editions if e.physical_format
|
||||||
|
),
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, "editions.html", data)
|
return TemplateResponse(request, "book/editions.html", data)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Directory(View):
|
||||||
data = {
|
data = {
|
||||||
"users": paginated.page(page),
|
"users": paginated.page(page),
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, "directory.html", data)
|
return TemplateResponse(request, "directory/directory.html", data)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
""" join the directory """
|
""" join the directory """
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
""" shelf views"""
|
""" shelf views"""
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.paginator import Paginator
|
||||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
@ -9,6 +10,7 @@ from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from bookwyrm import forms, models
|
from bookwyrm import forms, models
|
||||||
from bookwyrm.activitypub import ActivitypubResponse
|
from bookwyrm.activitypub import ActivitypubResponse
|
||||||
|
from bookwyrm.settings import PAGE_LENGTH
|
||||||
from .helpers import is_api_request, get_edition, get_user_from_username
|
from .helpers import is_api_request, get_edition, get_user_from_username
|
||||||
from .helpers import handle_reading_status
|
from .helpers import handle_reading_status
|
||||||
|
|
||||||
|
@ -24,6 +26,11 @@ class Shelf(View):
|
||||||
except models.User.DoesNotExist:
|
except models.User.DoesNotExist:
|
||||||
return HttpResponseNotFound()
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
try:
|
||||||
|
page = int(request.GET.get("page", 1))
|
||||||
|
except ValueError:
|
||||||
|
page = 1
|
||||||
|
|
||||||
if shelf_identifier:
|
if shelf_identifier:
|
||||||
shelf = user.shelf_set.get(identifier=shelf_identifier)
|
shelf = user.shelf_set.get(identifier=shelf_identifier)
|
||||||
else:
|
else:
|
||||||
|
@ -49,10 +56,11 @@ class Shelf(View):
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
return ActivitypubResponse(shelf.to_activity(**request.GET))
|
return ActivitypubResponse(shelf.to_activity(**request.GET))
|
||||||
|
|
||||||
books = (
|
paginated = Paginator(
|
||||||
models.ShelfBook.objects.filter(user=user, shelf=shelf)
|
models.ShelfBook.objects.filter(user=user, shelf=shelf)
|
||||||
.order_by("-updated_date")
|
.order_by("-updated_date")
|
||||||
.all()
|
.all(),
|
||||||
|
PAGE_LENGTH,
|
||||||
)
|
)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
|
@ -60,7 +68,7 @@ class Shelf(View):
|
||||||
"is_self": is_self,
|
"is_self": is_self,
|
||||||
"shelves": shelves.all(),
|
"shelves": shelves.all(),
|
||||||
"shelf": shelf,
|
"shelf": shelf,
|
||||||
"books": [b.book for b in books],
|
"books": paginated.page(page),
|
||||||
}
|
}
|
||||||
|
|
||||||
return TemplateResponse(request, "user/shelf.html", data)
|
return TemplateResponse(request, "user/shelf.html", data)
|
||||||
|
|
Loading…
Reference in a new issue