Merge branch 'main' into progress-modal

This commit is contained in:
Mouse Reeve 2021-09-29 09:53:02 -07:00
commit 4dbb09be87
104 changed files with 1818 additions and 1193 deletions

View file

@ -8,4 +8,4 @@ WORKDIR /app
COPY requirements.txt /app/
RUN pip install -r requirements.txt --no-cache-dir
RUN apt-get update && apt-get install -y gettext libgettextpo-dev && apt-get clean
RUN apt-get update && apt-get install -y gettext libgettextpo-dev tidy && apt-get clean

View file

@ -29,8 +29,7 @@ class CustomForm(ModelForm):
input_type = visible.field.widget.input_type
if isinstance(visible.field.widget, Textarea):
input_type = "textarea"
visible.field.widget.attrs["cols"] = None
visible.field.widget.attrs["rows"] = None
visible.field.widget.attrs["rows"] = 5
visible.field.widget.attrs["class"] = css_classes[input_type]
@ -269,7 +268,7 @@ class CreateInviteForm(CustomForm):
class ShelfForm(CustomForm):
class Meta:
model = models.Shelf
fields = ["user", "name", "privacy"]
fields = ["user", "name", "privacy", "description"]
class GoalForm(CustomForm):

View file

@ -0,0 +1,18 @@
# Generated by Django 3.2.5 on 2021-09-28 23:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0099_readthrough_is_active"),
]
operations = [
migrations.AddField(
model_name="shelf",
name="description",
field=models.TextField(blank=True, max_length=500, null=True),
),
]

View file

@ -21,6 +21,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
name = fields.CharField(max_length=100)
identifier = models.CharField(max_length=100)
description = models.TextField(blank=True, null=True, max_length=500)
user = fields.ForeignKey(
"User", on_delete=models.PROTECT, activitypub_field="owner"
)
@ -52,6 +53,11 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
"""list of books for this shelf, overrides OrderedCollectionMixin"""
return self.books.order_by("shelfbook")
@property
def deletable(self):
"""can the shelf be safely deleted?"""
return self.editable and not self.shelfbook_set.exists()
def get_remote_id(self):
"""shelf identifier instead of id"""
base_path = self.user.remote_id
@ -61,7 +67,7 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
def raise_not_deletable(self, viewer):
"""don't let anyone delete a default shelf"""
super().raise_not_deletable(viewer)
if not self.editable:
if not self.deletable:
raise PermissionDenied()
class Meta:

View file

@ -236,14 +236,12 @@
<label class="label" for="id_cover">{% trans "Upload cover:" %}</label>
{{ form.cover }}
</div>
{% if book %}
<div class="field">
<label class="label" for="id_cover_url">
{% trans "Load cover from url:" %}
</label>
<input class="input" name="cover-url" id="id_cover_url">
<input class="input" name="cover-url" id="id_cover_url" type="url" value="{{ cover_url|default:'' }}">
</div>
{% endif %}
{% for error in form.cover.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}

View file

@ -8,7 +8,7 @@
{% 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 %}>
<input type="radio" class="radio" name="scope" value="federated" {% if request.GET.scope == "federated" %}checked{% endif %}>
{% trans "Federated community" %}
</label>
{% endblock %}

View file

@ -5,8 +5,8 @@
<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>
<option value="recent" {% if request.GET.sort == "recent" %}selected{% endif %}>{% trans "Recently active" %}</option>
<option value="suggested" {% if request.GET.sort == "suggested" %}selected{% endif %}>{% trans "Suggested" %}</option>
</select>
</div>
{% endblock %}

View file

@ -117,7 +117,7 @@
</a>
</li>
{% if perms.bookwyrm.create_invites or perms.moderate_user %}
<li class="navbar-divider" role="presentation"></li>
<li class="navbar-divider" role="presentation">&nbsp;</li>
{% endif %}
{% if perms.bookwyrm.create_invites and not site.allow_registration %}
<li>
@ -133,7 +133,7 @@
</a>
</li>
{% endif %}
<li class="navbar-divider" role="presentation"></li>
<li class="navbar-divider" role="presentation">&nbsp;</li>
<li>
<a href="{% url 'logout' %}" class="navbar-item">
{% trans 'Log out' %}

View file

@ -9,7 +9,7 @@
{% block panel %}
{% if not request.user.blocks.exists %}
<p>{% trans "No users currently blocked." %}</p>
<p><em>{% trans "No users currently blocked." %}</em></p>
{% else %}
<ul>
{% for user in request.user.blocks.all %}

View file

@ -10,11 +10,11 @@
{% block panel %}
<form name="edit-profile" action="{% url 'prefs-password' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="block">
<div class="field">
<label class="label" for="id_password">{% trans "New password:" %}</label>
<input type="password" name="password" maxlength="128" class="input" required="" id="id_password">
</div>
<div class="block">
<div class="field">
<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">
</div>

View file

@ -7,46 +7,72 @@
{% trans "Edit Profile" %}
{% endblock %}
{% block profile-tabs %}
<ul class="menu-list">
<li><a href="#profile">{% trans "Profile" %}</a></li>
<li><a href="#display-preferences">{% trans "Display preferences" %}</a></li>
<li><a href="#privacy">{% trans "Privacy" %}</a></li>
</ul>
{% endblock %}
{% block panel %}
{% if form.non_field_errors %}
<p class="notification is-danger">{{ form.non_field_errors }}</p>
{% endif %}
<form name="edit-profile" action="{% url 'prefs-profile' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="block">
<section class="block" id="profile">
<h2 class="title is-4">{% trans "Profile" %}</h2>
<div class="box">
<label class="label" for="id_avatar">{% trans "Avatar:" %}</label>
<div class="field columns is-mobile">
{% if request.user.avatar %}
<div class="column is-narrow">
{% include 'snippets/avatar.html' with user=request.user large=True %}
</div>
{% endif %}
<div class="column">
{{ form.avatar }}
{% for error in form.avatar.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
</div>
<div class="field">
<label class="label" for="id_name">{% trans "Display name:" %}</label>
{{ form.name }}
{% for error in form.name.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<div class="field">
<label class="label" for="id_summary">{% trans "Summary:" %}</label>
{{ form.summary }}
{% for error in form.summary.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
<div class="field">
<label class="label" for="id_email">{% trans "Email address:" %}</label>
{{ form.email }}
{% for error in form.email.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<div class="block">
</div>
</section>
<hr aria-hidden="true">
<section class="block" id="display-preferences">
<h2 class="title is-4">{% trans "Display preferences" %}</h2>
<div class="box">
<div class="field">
<label class="checkbox label" for="id_show_goal">
{% trans "Show reading goal prompt in feed:" %}
{{ form.show_goal }}
</label>
<label class="checkbox label" for="id_show_goal">
<label class="checkbox label" for="id_show_suggested_users">
{% trans "Show suggested users:" %}
{{ form.show_suggested_users }}
</label>
@ -55,15 +81,31 @@
{{ form.discoverable }}
</label>
{% url 'directory' as path %}
<p class="help">{% blocktrans %}Your account will show up in the <a href="{{ path }}">directory</a>, and may be recommended to other BookWyrm users.{% endblocktrans %}</p>
<p class="help">
{% blocktrans %}Your account will show up in the <a href="{{ path }}">directory</a>, and may be recommended to other BookWyrm users.{% endblocktrans %}
</p>
</div>
<div class="block">
<div class="field">
<label class="label" for="id_preferred_timezone">{% trans "Preferred Timezone: " %}</label>
<div class="select">
{{ form.preferred_timezone }}
</div>
</div>
</div>
</section>
<hr aria-hidden="true">
<section class="block" id="privacy">
<h2 class="title is-4">{% trans "Privacy" %}</h2>
<div class="box">
<div class="field">
<label class="checkbox label" for="id_manually_approves_followers">
{% trans "Manually approve followers:" %}
{{ form.manually_approves_followers }}
</label>
</div>
<div class="block">
<div class="field">
<label class="label" for="id_default_post_privacy">
{% trans "Default post privacy:" %}
</label>
@ -71,12 +113,8 @@
{{ form.default_post_privacy }}
</div>
</div>
<div class="block">
<label class="label" for="id_preferred_timezone">{% trans "Preferred Timezone: " %}</label>
<div class="select">
{{ form.preferred_timezone }}
</div>
</div>
<div class="block"><button class="button is-primary" type="submit">{% trans "Save" %}</button></div>
</section>
<div class="field"><button class="button is-primary" type="submit">{% trans "Save" %}</button></div>
</form>
{% endblock %}

View file

@ -12,7 +12,8 @@
<ul class="menu-list">
<li>
{% url 'prefs-profile' as url %}
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Profile" %}</a>
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Edit Profile" %}</a>
{% block profile-tabs %}{% endblock %}
</li>
<li>
{% url 'prefs-password' as url %}

View file

@ -26,7 +26,7 @@
{% block panel %}
<form name="edit-announcement" method="post" action="{% url 'settings-announcements' announcement.id %}" class="block">
{% include 'settings/announcement_form.html' with controls_text="edit_announcement" %}
{% include 'settings/announcements/announcement_form.html' with controls_text="edit_announcement" %}
</form>
<div class="block content">

View file

@ -11,7 +11,7 @@
{% block panel %}
<form name="create-announcement" method="post" action="{% url 'settings-announcements' %}" class="block">
{% include 'settings/announcement_form.html' with controls_text="create_announcement" %}
{% include 'settings/announcements/announcement_form.html' with controls_text="create_announcement" %}
</form>
<div class="block">
@ -48,11 +48,10 @@
<td>{% if announcement.active %}{% trans "active" %}{% else %}{% trans "inactive" %}{% endif %}</td>
</tr>
{% endfor %}
</table>
{% if not announcements %}
<p><em>{% trans "No announcements found." %}</em></p>
<tr><td colspan="5"><em>{% trans "No announcements found" %}</em></td></tr>
{% endif %}
</table>
</div>
{% include 'snippets/pagination.html' with page=announcements path=request.path %}

View file

@ -67,27 +67,27 @@
<form method="get" action="{% url 'settings-dashboard' %}" class="notification has-background-white-bis">
<div class="is-flex is-align-items-flex-end">
<div class="ml-1 mr-1">
<label class="label">
<label class="label" for="id_start">
{% trans "Start date:" %}
<input class="input" type="date" name="start" value="{{ start }}">
</label>
<input class="input" type="date" name="start" value="{{ start }}" id="id_start">
</div>
<div class="ml-1 mr-1">
<label class="label">
<label class="label" for="id_end">
{% trans "End date:" %}
<input class="input" type="date" name="end" value="{{ end }}">
</label>
<input class="input" type="date" name="end" value="{{ end }}" id="id_end">
</div>
<div class="ml-1 mr-1">
<label class="label">
<label class="label" for="id_interval">
{% trans "Interval:" %}
</label>
<div class="select">
<select name="days">
<select name="days" id="id_interval">
<option value="1" {% if interval == 1 %}selected{% endif %}>{% trans "Days" %}</option>
<option value="7" {% if interval == 7 %}selected{% endif %}>{% trans "Weeks" %}</option>
</select>
</div>
</label>
</div>
<div class="ml-1 mr-1">
<button class="button is-link" type="submit">{% trans "Submit" %}</button>
@ -115,6 +115,6 @@
{% block scripts %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
{% include 'settings/dashboard_user_chart.html' %}
{% include 'settings/dashboard_status_chart.html' %}
{% include 'settings/dashboard/dashboard_user_chart.html' %}
{% include 'settings/dashboard/dashboard_status_chart.html' %}
{% endblock %}

View file

@ -12,7 +12,7 @@
{% endblock %}
{% block panel %}
{% include 'settings/domain_form.html' with controls_text="add_domain" class="block" %}
{% include 'settings/email_blocklist/domain_form.html' with controls_text="add_domain" class="block" %}
<p class="notification block">
{% trans "When someone tries to register with an email from this domain, no account will be created. The registration process will appear to have worked." %}
@ -55,7 +55,11 @@
</td>
</tr>
{% endfor %}
{% if not domains.exists %}
<tr><td colspan="5"><em>{% trans "No email domains currently blocked" %}</em></td></tr>
{% endif %}
</table>
{% endblock %}

View file

@ -33,6 +33,8 @@
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="label" for="id_status">{% trans "Status:" %}</label>
<div class="select">
@ -43,6 +45,8 @@
</div>
</div>
</div>
</div>
<div class="columns">
<div class="column is-half">
<div class="field">
<label class="label" for="id_application_type">{% trans "Software:" %}</label>
@ -51,6 +55,8 @@
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
</div>
<div class="column is-half">
<div class="field">
<label class="label" for="id_application_version">{% trans "Version:" %}</label>
<input type="text" name="application_version" maxlength="255" class="input" id="id_application_version" value="{{ form.application_version.value|default:'' }}">
@ -62,7 +68,7 @@
</div>
<div class="field">
<label class="label" for="id_notes">{% trans "Notes:" %}</label>
<textarea name="notes" cols="None" rows="None" class="textarea" id="id_notes">{{ form.notes.value|default:'' }}</textarea>
<textarea name="notes" cols="40" rows="5" class="textarea" id="id_notes">{{ form.notes.value|default:'' }}</textarea>
</div>
<button type="submit" class="button is-primary">{% trans "Save" %}</button>

View file

@ -19,18 +19,14 @@
<h2 class="title is-4">{% trans "Details" %}</h2>
<div class="box is-flex-grow-1 content">
<dl>
<div class="is-flex">
<dt>{% trans "Software:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Software:" %}</dt>
<dd>{{ server.application_type }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Version:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Version:" %}</dt>
<dd>{{ server.application_version }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Status:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Status:" %}</dt>
<dd>{{ server.get_status_display }}</dd>
</div>
</dl>
</div>
</section>
@ -39,38 +35,32 @@
<h2 class="title is-4">{% trans "Activity" %}</h2>
<div class="box is-flex-grow-1 content">
<dl>
<div class="is-flex">
<dt>{% trans "Users:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Users:" %}</dt>
<dd>
{{ users.count }}
{% if server.user_set.count %}(<a href="{% url 'settings-users' %}?server={{ server.server_name }}">{% trans "View all" %}</a>){% endif %}
</dd>
</div>
<div class="is-flex">
<dt>{% trans "Reports:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Reports:" %}</dt>
<dd>
{{ reports.count }}
{% if reports.count %}(<a href="{% url 'settings-reports' %}?server={{ server.server_name }}">{% trans "View all" %}</a>){% endif %}
</dd>
</div>
<div class="is-flex">
<dt>{% trans "Followed by us:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Followed by us:" %}</dt>
<dd>
{{ followed_by_us.count }}
</dd>
</div>
<div class="is-flex">
<dt>{% trans "Followed by them:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Followed by them:" %}</dt>
<dd>
{{ followed_by_them.count }}
</dd>
</div>
<div class="is-flex">
<dt>{% trans "Blocked by us:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Blocked by us:" %}</dt>
<dd>
{{ blocked_by_us.count }}
</dd>
</div>
</dl>
</div>
</section>
@ -86,14 +76,13 @@
{% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="pencil" controls_text="edit_notes" %}
</div>
</header>
{% if server.notes %}
<div class="box" id="hide_edit_notes">{{ server.notes|to_markdown|safe }}</div>
{% endif %}
{% trans "<em>No notes</em>" as null_text %}
<div class="box" id="hide_edit_notes">{{ server.notes|to_markdown|default:null_text|safe }}</div>
<form class="box is-hidden" method="POST" action="{% url 'settings-federated-server' server.id %}" id="edit_notes">
{% csrf_token %}
<p>
<label class="is-sr-only" for="id_notes">Notes:</label>
<textarea name="notes" cols="None" rows="None" class="textarea" id="id_notes">{{ server.notes|default:"" }}</textarea>
<textarea name="notes" cols="40" rows="5" class="textarea" id="id_notes">{{ server.notes|default:"" }}</textarea>
</p>
<button type="submit" class="button is-primary">{% trans "Save" %}</button>
{% trans "Cancel" as button_text %}

View file

@ -59,7 +59,11 @@
<td>{{ server.get_status_display }}</td>
</tr>
{% endfor %}
{% if not servers %}
<tr><td colspan="5"><em>{% trans "No instances found" %}</em></td></tr>
{% endif %}
</table>
{% include 'snippets/pagination.html' with page=servers path=request.path %}
{% endblock %}

View file

@ -1,6 +1,6 @@
{% extends 'snippets/filters_panel/filters_panel.html' %}
{% block filter_fields %}
{% include 'settings/status_filter.html' %}
{% include 'settings/invites/status_filter.html' %}
{% endblock %}

View file

@ -26,7 +26,7 @@
{% endif %} ({{ count }})
</h2>
{% include 'settings/invite_request_filters.html' %}
{% include 'settings/invites/invite_request_filters.html' %}
<table class="table is-striped is-fullwidth">
{% url 'settings-invite-requests' as url %}
@ -47,7 +47,7 @@
<th>{% trans "Action" %}</th>
</tr>
{% if not requests %}
<tr><td colspan="4">{% trans "No requests" %}</td></tr>
<tr><td colspan="5"><em>{% trans "No requests" %}</em></td></tr>
{% endif %}
{% for req in requests %}
<tr>

View file

@ -12,7 +12,7 @@
{% endblock %}
{% block panel %}
{% include 'settings/ip_address_form.html' with controls_text="add_address" class="block" %}
{% include 'settings/ip_blocklist/ip_address_form.html' with controls_text="add_address" class="block" %}
<p class="notification block">
{% trans "Any traffic from this IP address will get a 404 response when trying to access any part of the application." %}
@ -42,6 +42,9 @@
</td>
</tr>
{% endfor %}
{% if not addresses.exists %}
<tr><td colspan="2"><em>{% trans "No IP addresses currently blocked" %}</em></td></tr>
{% endif %}
</table>
{% endblock %}

View file

@ -74,14 +74,7 @@
<li>
{% url 'settings-site' as url %}
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Site Settings" %}</a>
{% if url in request.path %}
<ul class="emnu-list">
<li><a href="{{ url }}#instance-info">{% trans "Instance Info" %}</a></li>
<li><a href="{{ url }}#images">{% trans "Images" %}</a></li>
<li><a href="{{ url }}#footer">{% trans "Footer Content" %}</a></li>
<li><a href="{{ url }}#registration">{% trans "Registration" %}</a></li>
</ul>
{% endif %}
{% block site-subtabs %}{% endblock %}
</li>
</ul>
{% endif %}

View file

@ -3,20 +3,21 @@
{% load humanize %}
{% block title %}{% blocktrans with report_id=report.id username=report.user.username %}Report #{{ report_id }}: {{ username }}{% endblocktrans %}{% endblock %}
{% block header %}{% blocktrans with report_id=report.id username=report.user.username %}Report #{{ report_id }}: {{ username }}{% endblocktrans %}{% endblock %}
{% block header %}
{% blocktrans with report_id=report.id username=report.user.username %}Report #{{ report_id }}: {{ username }}{% endblocktrans %}
<a href="{% url 'settings-reports' %}" class="has-text-weight-normal help">{% trans "Back to reports" %}</a>
{% endblock %}
{% block panel %}
<div class="block">
<a href="{% url 'settings-reports' %}">{% trans "Back to reports" %}</a>
</div>
<div class="block">
{% include 'moderation/report_preview.html' with report=report %}
{% include 'settings/reports/report_preview.html' with report=report %}
</div>
{% include 'user_admin/user_info.html' with user=report.user %}
{% include 'settings/users/user_info.html' with user=report.user %}
{% include 'user_admin/user_moderation_actions.html' with user=report.user %}
{% include 'settings/users/user_moderation_actions.html' with user=report.user %}
<div class="block">
<h3 class="title is-4">{% trans "Moderator Comments" %}</h3>

View file

@ -30,7 +30,7 @@
</ul>
</div>
{% include 'user_admin/user_admin_filters.html' %}
{% include 'settings/users/user_admin_filters.html' %}
<div class="block">
{% if not reports %}
@ -39,7 +39,7 @@
{% for report in reports %}
<div class="block">
{% include 'moderation/report_preview.html' with report=report %}
{% include 'settings/reports/report_preview.html' with report=report %}
</div>
{% endfor %}
</div>

View file

@ -5,12 +5,21 @@
{% block header %}{% trans "Site Settings" %}{% endblock %}
{% block panel %}
{% block site-subtabs %}
<ul class="menu-list">
<li><a href="#instance-info">{% trans "Instance Info" %}</a></li>
<li><a href="#images">{% trans "Images" %}</a></li>
<li><a href="#footer">{% trans "Footer Content" %}</a></li>
<li><a href="#registration">{% trans "Registration" %}</a></li>
</ul>
{% endblock %}
{% block panel %}
<form action="{% url 'settings-site' %}" method="POST" class="content" enctype="multipart/form-data">
{% csrf_token %}
<section class="block" id="instance_info">
<h2 class="title is-4">{% trans "Instance Info" %}</h2>
<div class="box">
<div class="field">
<label class="label" for="id_name">{% trans "Instance Name:" %}</label>
{{ site_form.name }}
@ -36,22 +45,23 @@
<label class="label" for="id_privacy_policy">{% trans "Privacy Policy:" %}</label>
{{ site_form.privacy_policy }}
</div>
</div>
</section>
<hr aria-hidden="true">
<section class="block" id="images">
<h2 class="title is-4">{% trans "Images" %}</h2>
<div class="columns">
<div class="column">
<div class="box is-flex">
<div>
<label class="label" for="id_logo">{% trans "Logo:" %}</label>
{{ site_form.logo }}
</div>
<div class="column">
<div>
<label class="label" for="id_logo_small">{% trans "Logo small:" %}</label>
{{ site_form.logo_small }}
</div>
<div class="column">
<div>
<label class="label" for="id_favicon">{% trans "Favicon:" %}</label>
{{ site_form.favicon }}
</div>
@ -62,6 +72,7 @@
<section class="block" id="footer">
<h2 class="title is-4">{% trans "Footer Content" %}</h2>
<div class="box">
<div class="field">
<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 %}>
@ -78,12 +89,14 @@
<label class="label" for="id_footer_item">{% trans "Additional info:" %}</label>
{{ site_form.footer_item }}
</div>
</div>
</section>
<hr aria-hidden="true">
<section class="block" id="registration">
<h2 class="title is-4">{% trans "Registration" %}</h2>
<div class="box">
<div class="field">
<label class="label" for="id_allow_registration">
{{ site_form.allow_registration }}
@ -97,7 +110,7 @@
</label>
</div>
<div class="field">
<label class="label mb-0" for="id_allow_invite_requests">
<label class="label mb-0" for="id_require_confirm_email">
{{ site_form.require_confirm_email }}
{% trans "Require users to confirm email address" %}
</label>
@ -114,6 +127,7 @@
<p class="help is-danger">{{ error|escape }}</p>
{% endfor %}
</div>
</div>
</section>
<footer class="block">

View file

@ -0,0 +1,16 @@
{% extends 'settings/layout.html' %}
{% load i18n %}
{% block title %}{{ user.username }}{% endblock %}
{% block header %}
{{ user.username }}
<a class="help has-text-weight-normal" href="{% url 'settings-users' %}">{% trans "Back to users" %}</a>
{% endblock %}
{% block panel %}
{% include 'settings/users/user_info.html' with user=user %}
{% include 'settings/users/user_moderation_actions.html' with user=user %}
{% endblock %}

View file

@ -13,7 +13,7 @@
{% block panel %}
{% include 'user_admin/user_admin_filters.html' %}
{% include 'settings/users/user_admin_filters.html' %}
<table class="table is-striped">
<tr>

View file

@ -1,7 +1,7 @@
{% extends 'snippets/filters_panel/filters_panel.html' %}
{% block filter_fields %}
{% include 'user_admin/username_filter.html' %}
{% include 'settings/users/username_filter.html' %}
{% include 'directory/community_filter.html' %}
{% include 'user_admin/server_filter.html' %}
{% include 'settings/users/server_filter.html' %}
{% endblock %}

View file

@ -48,15 +48,12 @@
<div class="box content is-flex-grow-1">
<dl>
{% if user.local %}
<div class="is-flex">
<dt>{% trans "Email:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Email:" %}</dt>
<dd>{{ user.email }}</dd>
</div>
{% endif %}
{% with report_count=user.report_set.count %}
<div class="is-flex">
<dt>{% trans "Reports:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Reports:" %}</dt>
<dd>
{{ report_count|intcomma }}
{% if report_count > 0 %}
@ -65,41 +62,28 @@
</a>
{% endif %}
</dd>
</div>
{% endwith %}
<div class="is-flex">
<dt>{% trans "Blocked by count:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Blocked by count:" %}</dt>
<dd>{{ user.blocked_by.count }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Last active date:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Last active date:" %}</dt>
<dd>{{ user.last_active_date }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Manually approved followers:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Manually approved followers:" %}</dt>
<dd>{{ user.manually_approves_followers }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Discoverable:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Discoverable:" %}</dt>
<dd>{{ user.discoverable }}</dd>
</div>
{% if not user.is_active %}
<div class="is-flex">
<dt>{% trans "Deactivation reason:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Deactivation reason:" %}</dt>
<dd>{{ user.deactivation_reason }}</dd>
</div>
{% endif %}
{% if not user.is_active and user.deactivation_reason == "pending" %}
<div class="is-flex">
<dt>{% trans "Confirmation code:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Confirmation code:" %}</dt>
<dd>{{ user.confirmation_code }}</dd>
</div>
{% endif %}
</dl>
</div>
@ -113,18 +97,14 @@
{% if server %}
<h5>{{ server.server_name }}</h5>
<dl>
<div class="is-flex">
<dt>{% trans "Software:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Software:" %}</dt>
<dd>{{ server.application_type }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Version:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Version:" %}</dt>
<dd>{{ server.application_version }}</dd>
</div>
<div class="is-flex">
<dt>{% trans "Status:" %}</dt>
<dt class="is-pulled-left mr-5">{% trans "Status:" %}</dt>
<dd>{{ server.status }}</dd>
</div>
</dl>
{% if server.notes %}
<h5>{% trans "Notes" %}</h5>

View file

@ -36,7 +36,7 @@
{% if user.local %}
<div>
{% include "user_admin/delete_user_form.html" with controls_text="delete_user" class="mt-2 mb-2" %}
{% include "settings/users/delete_user_form.html" with controls_text="delete_user" class="mt-2 mb-2" %}
</div>
{% endif %}

View file

@ -0,0 +1,13 @@
{% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %}
{% trans "Create Shelf" %}
{% endblock %}
{% block form %}
<form name="create-shelf" action="{% url 'shelf-create' %}" method="post">
{% include "shelf/form.html" with editable=shelf.editable form=create_form %}
</form>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %}
{% trans "Edit Shelf" %}
{% endblock %}
{% block form %}
<form name="edit-shelf" action="{{ shelf.local_path }}" method="post">
{% include "shelf/form.html" with editable=shelf.editable form=edit_form privacy=shelf.privacy %}
</form>
{% endblock %}

View file

@ -0,0 +1,28 @@
{% load i18n %}
{% load utilities %}
{% with 0|uuid as uuid %}
{% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}">
{% if editable %}
<div class="field">
<label class="label" for="id_name">{% trans "Name:" %}</label>
<input type="text" name="name" value="{{ form.name.value|default:'' }}" maxlength="100" class="input" required="" id="id_name">
</div>
{% else %}
<input type="hidden" name="name" required="true" value="{{ shelf.name }}">
{% endif %}
<div class="field">
<label class="label" for="id_description_{{ uuid }}">{% trans "Description:" %}</label>
<textarea name="description" cols="40" rows="5" maxlength="500" class="textarea" id="id_description_{{ uuid }}">{{ form.description.value|default:'' }}</textarea>
</div>
<div class="field has-addons">
<div class="control">
{% include 'snippets/privacy_select.html' with current=privacy %}
</div>
<div class="control">
<button class="button is-primary" type="submit">{% trans "Save" %}</button>
</div>
</div>
{% endwith %}

View file

@ -5,7 +5,7 @@
{% load i18n %}
{% block title %}
{% include 'user/shelf/books_header.html' %}
{% include 'user/books_header.html' %}
{% endblock %}
{% block opengraph_images %}
@ -15,7 +15,7 @@
{% block content %}
<header class="block">
<h1 class="title">
{% include 'user/shelf/books_header.html' %}
{% include 'user/books_header.html' %}
</h1>
</header>
@ -60,9 +60,10 @@
</div>
<div class="block">
{% include 'user/shelf/create_shelf_form.html' with controls_text='create_shelf_form' %}
{% include 'shelf/create_shelf_form.html' with controls_text='create_shelf_form' %}
</div>
<div>
<div class="block columns is-mobile">
<div class="column">
<h2 class="title is-3">
@ -91,14 +92,30 @@
</div>
{% if is_self and shelf.id %}
<div class="column is-narrow">
<div class="is-flex">
{% trans "Edit shelf" as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text icon_with_text="pencil" controls_text="edit_shelf_form" focus="edit_shelf_form_header" %}
{% if shelf.deletable %}
<form class="ml-1" 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 %}
</div>
</div>
{% endif %}
</div>
{% if shelf.description %}
<p>{{ shelf.description }}</p>
{% endif %}
</div>
<div class="block">
{% include 'user/shelf/edit_shelf_form.html' with controls_text="edit_shelf_form" %}
{% include 'shelf/edit_shelf_form.html' with controls_text="edit_shelf_form" %}
</div>
<div class="block">
@ -167,17 +184,7 @@
</tbody>
</table>
{% else %}
<p>{% trans "This shelf is empty." %}</p>
{% if shelf.id and 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 %}
<p><em>{% trans "This shelf is empty." %}</em></p>
{% endif %}
</div>

View file

@ -1,7 +1,7 @@
{% load i18n %}
{% load utilities %}
<div class="select {{ class }}">
{% with 0|uuid as uuid %}
{% firstof uuid 0|uuid as uuid %}
{% if not no_label %}
<label class="is-sr-only" for="privacy_{{ uuid }}">{% trans "Post privacy" %}</label>
{% endif %}
@ -20,6 +20,5 @@
{% trans "Private" %}
</option>
</select>
{% endwith %}
</div>

View file

@ -6,6 +6,6 @@
{% trans "Report" as button_text %}
{% include 'snippets/toggle/toggle_button.html' with class="is-danger is-light is-small is-fullwidth" text=button_text controls_text="report" controls_uid=report_uuid focus="modal_title_report" disabled=is_current %}
{% include 'moderation/report_modal.html' with user=user reporter=request.user controls_text="report" controls_uid=report_uuid %}
{% include 'snippets/report_modal.html' with user=user reporter=request.user controls_text="report" controls_uid=report_uuid %}
{% endwith %}

View file

@ -9,6 +9,6 @@
{% block nullstate %}
<div>
{% blocktrans with username=user.display_name %}{{ username }} has no followers{% endblocktrans %}
<em>{% blocktrans with username=user.display_name %}{{ username }} has no followers{% endblocktrans %}</em>
</div>
{% endblock %}

View file

@ -9,7 +9,7 @@
{% block nullstate %}
<div>
{% blocktrans with username=user.display_name %}{{ username }} isn't following any users{% endblocktrans %}
<em>{% blocktrans with username=user.display_name %}{{ username }} isn't following any users{% endblocktrans %}</em>
</div>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %}
{% trans "Create Shelf" %}
{% endblock %}
{% block form %}
<form name="create-shelf" action="{% url 'shelf-create' %}" method="post">
{% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}">
<div class="field">
<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">
</div>
<div class="field has-addons">
<div class="control">
{% include 'snippets/privacy_select.html' %}
</div>
<div class="control">
<button class="button is-primary" type="submit">{% trans "Create Shelf" %}</button>
</div>
</div>
</form>
{% endblock %}

View file

@ -1,31 +0,0 @@
{% extends 'components/inline_form.html' %}
{% load i18n %}
{% block header %}
{% trans "Edit Shelf" %}
{% endblock %}
{% block form %}
<form name="edit-shelf" action="{{ shelf.local_path }}" method="post">
{% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}">
{% if shelf.editable %}
<div class="field">
<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">
</div>
{% else %}
<input type="hidden" name="name" required="true" value="{{ shelf.name }}">
{% endif %}
<div class="field has-addons">
<div class="control">
{% include 'snippets/privacy_select.html' with current=shelf.privacy %}
</div>
<div class="control">
<button class="button is-primary" type="submit">{% trans "Update shelf" %}</button>
</div>
</div>
</form>
{% endblock %}

View file

@ -24,7 +24,7 @@
{% if user.bookwyrm_user %}
<div class="block">
<h2 class="title">
{% include 'user/shelf/books_header.html' %}
{% include 'user/books_header.html' %}
</h2>
<div class="columns is-mobile scroll-x">
{% for shelf in shelves %}

View file

@ -1,19 +0,0 @@
{% extends 'settings/layout.html' %}
{% load i18n %}
{% block title %}{{ user.username }}{% endblock %}
{% block header %}
{{ user.username }}
<p class="help has-text-weight-normal">
<a href="{% url 'settings-users' %}">{% trans "Back to users" %}</a>
</p>
{% endblock %}
{% block panel %}
{% include 'user_admin/user_info.html' with user=user %}
{% include 'user_admin/user_moderation_actions.html' with user=user %}
{% endblock %}

View file

@ -0,0 +1 @@
from . import *

View file

@ -1,5 +1,6 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
@ -34,5 +35,8 @@ class DashboardViews(TestCase):
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)

View file

@ -1,5 +1,7 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
@ -36,7 +38,10 @@ class EmailBlocklistViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_blocklist_page_post(self):
@ -49,7 +54,10 @@ class EmailBlocklistViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
self.assertTrue(

View file

@ -1,6 +1,8 @@
""" test for app action functionality """
import json
from unittest.mock import patch
from tidylib import tidy_document
from django.core.files.uploadedfile import SimpleUploadedFile
from django.template.response import TemplateResponse
from django.test import TestCase
@ -46,10 +48,19 @@ class FederationViews(TestCase):
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_server_page(self):
def test_instance_page(self):
"""there are so many views, this just makes sure it LOADS"""
server = models.FederatedServer.objects.create(server_name="hi.there.com")
view = views.FederatedServer.as_view()
@ -59,7 +70,10 @@ class FederationViews(TestCase):
result = view(request, server.id)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_server_page_block(self):
@ -148,7 +162,10 @@ class FederationViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_add_view_post_create(self):
@ -169,6 +186,7 @@ class FederationViews(TestCase):
self.assertEqual(server.application_type, "coolsoft")
self.assertEqual(server.status, "blocked")
# pylint: disable=consider-using-with
def test_import_blocklist(self):
"""load a json file with a list of servers to block"""
server = models.FederatedServer.objects.create(server_name="hi.there.com")
@ -180,7 +198,7 @@ class FederationViews(TestCase):
{"instance": "hi.there.com", "url": "https://explanation.url"}, # existing
{"a": "b"}, # invalid
]
json.dump(data, open("file.json", "w"))
json.dump(data, open("file.json", "w")) # pylint: disable=unspecified-encoding
view = views.ImportServerBlocklist.as_view()
request = self.factory.post(

View file

@ -0,0 +1,44 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import models, views
class IPBlocklistViews(TestCase):
"""every response to a get request, html or json"""
def setUp(self):
"""we need basic test data and mocks"""
self.factory = RequestFactory()
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse@local.com",
"mouse@mouse.mouse",
"password",
local=True,
localname="mouse",
)
models.SiteSettings.objects.create()
def test_blocklist_page_get(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.IPBlocklist.as_view()
request = self.factory.get("")
request.user = self.local_user
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)

View file

@ -1,6 +1,8 @@
""" test for app action functionality """
import json
from unittest.mock import patch
from tidylib import tidy_document
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
@ -42,7 +44,16 @@ class ReportViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_reports_page_with_data(self):
@ -55,7 +66,16 @@ class ReportViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_report_page(self):
@ -69,7 +89,10 @@ class ReportViews(TestCase):
result = view(request, report.id)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_report_comment(self):

View file

@ -1,5 +1,7 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.contrib.auth.models import Group
from django.template.response import TemplateResponse
from django.test import TestCase
@ -34,7 +36,10 @@ class UserAdminViews(TestCase):
request.user.is_superuser = True
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_user_admin_page(self):
@ -47,7 +52,10 @@ class UserAdminViews(TestCase):
result = view(request, self.local_user.id)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@ -69,7 +77,10 @@ class UserAdminViews(TestCase):
result = view(request, self.local_user.id)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(
list(self.local_user.groups.values_list("name", flat=True)), ["editor"]

View file

@ -0,0 +1 @@
from . import *

View file

@ -1,5 +1,7 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
@ -44,7 +46,10 @@ class BlockViews(TestCase):
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_block_post(self, _):
@ -75,6 +80,6 @@ class BlockViews(TestCase):
request.user = self.local_user
with patch("bookwyrm.activitystreams.add_user_statuses_task.delay"):
views.block.unblock(request, self.remote_user.id)
views.unblock(request, self.remote_user.id)
self.assertFalse(models.UserBlocks.objects.exists())

View file

@ -0,0 +1,61 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import models, views
class ChangePasswordViews(TestCase):
"""view user and edit profile"""
def setUp(self):
"""we need basic test data and mocks"""
self.factory = RequestFactory()
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse@local.com",
"mouse@mouse.com",
"password",
local=True,
localname="mouse",
)
models.SiteSettings.objects.create(id=1)
def test_password_change_get(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.ChangePassword.as_view()
request = self.factory.get("")
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
html = result.render()
_, errors = tidy_document(html.content)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_password_change(self):
"""change password"""
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post("", {"password": "hi", "confirm-password": "hi"})
request.user = self.local_user
with patch("bookwyrm.views.preferences.change_password.login"):
view(request)
self.assertNotEqual(self.local_user.password, password_hash)
def test_password_change_mismatch(self):
"""change password"""
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post("", {"password": "hi", "confirm-password": "hihi"})
request.user = self.local_user
view(request)
self.assertEqual(self.local_user.password, password_hash)

View file

@ -0,0 +1,89 @@
""" test for app action functionality """
import json
from unittest.mock import patch
from tidylib import tidy_document
from django.contrib.sessions.middleware import SessionMiddleware
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import forms, models, views
@patch("bookwyrm.suggested_users.remove_user_task.delay")
class DeleteUserViews(TestCase):
"""view user and edit profile"""
def setUp(self):
"""we need basic test data and mocks"""
self.factory = RequestFactory()
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.local_user = models.User.objects.create_user(
"mouse@local.com",
"mouse@mouse.mouse",
"password",
local=True,
localname="mouse",
)
self.rat = models.User.objects.create_user(
"rat@local.com", "rat@rat.rat", "password", local=True, localname="rat"
)
self.book = models.Edition.objects.create(
title="test", parent_work=models.Work.objects.create(title="test work")
)
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"), patch(
"bookwyrm.activitystreams.add_book_statuses_task.delay"
):
models.ShelfBook.objects.create(
book=self.book,
user=self.local_user,
shelf=self.local_user.shelf_set.first(),
)
models.SiteSettings.objects.create()
def test_delete_user_page(self, _):
"""there are so many views, this just makes sure it LOADS"""
view = views.DeleteUser.as_view()
request = self.factory.get("")
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
html = result.render()
_, errors = tidy_document(html.content)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
@patch("bookwyrm.suggested_users.rerank_suggestions_task")
def test_delete_user(self, *_):
"""use a form to update a user"""
view = views.DeleteUser.as_view()
form = forms.DeleteUserForm()
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.local_user
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
self.assertIsNone(self.local_user.name)
with patch(
"bookwyrm.models.activitypub_mixin.broadcast_task.delay"
) as delay_mock:
view(request)
self.assertEqual(delay_mock.call_count, 1)
activity = json.loads(delay_mock.call_args[0][1])
self.assertEqual(activity["type"], "Delete")
self.assertEqual(activity["actor"], self.local_user.remote_id)
self.assertEqual(
activity["cc"][0], "https://www.w3.org/ns/activitystreams#Public"
)
self.local_user.refresh_from_db()
self.assertFalse(self.local_user.is_active)
self.assertEqual(self.local_user.deactivation_reason, "self_deletion")

View file

@ -1,11 +1,10 @@
""" test for app action functionality """
import json
import pathlib
from unittest.mock import patch
from PIL import Image
from tidylib import tidy_document
from django.contrib.auth.models import AnonymousUser
from django.contrib.sessions.middleware import SessionMiddleware
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import SimpleUploadedFile
from django.template.response import TemplateResponse
@ -59,7 +58,10 @@ class EditUserViews(TestCase):
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_edit_user(self, _):
@ -91,8 +93,9 @@ class EditUserViews(TestCase):
form.data["default_post_privacy"] = "public"
form.data["preferred_timezone"] = "UTC"
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/no_cover.jpg"
"../../../static/images/no_cover.jpg"
)
# pylint: disable=consider-using-with
form.data["avatar"] = SimpleUploadedFile(
image_file, open(image_file, "rb").read(), content_type="image/jpeg"
)
@ -113,50 +116,11 @@ class EditUserViews(TestCase):
def test_crop_avatar(self, _):
"""reduce that image size"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/no_cover.jpg"
"../../../static/images/no_cover.jpg"
)
image = Image.open(image_file)
result = views.edit_user.crop_avatar(image)
result = views.preferences.edit_user.crop_avatar(image)
self.assertIsInstance(result, ContentFile)
image_result = Image.open(result)
self.assertEqual(image_result.size, (120, 120))
def test_delete_user_page(self, _):
"""there are so many views, this just makes sure it LOADS"""
view = views.DeleteUser.as_view()
request = self.factory.get("")
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
self.assertEqual(result.status_code, 200)
@patch("bookwyrm.suggested_users.rerank_suggestions_task")
def test_delete_user(self, *_):
"""use a form to update a user"""
view = views.DeleteUser.as_view()
form = forms.DeleteUserForm()
form.data["password"] = "password"
request = self.factory.post("", form.data)
request.user = self.local_user
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
self.assertIsNone(self.local_user.name)
with patch(
"bookwyrm.models.activitypub_mixin.broadcast_task.delay"
) as delay_mock:
view(request)
self.assertEqual(delay_mock.call_count, 1)
activity = json.loads(delay_mock.call_args[0][1])
self.assertEqual(activity["type"], "Delete")
self.assertEqual(activity["actor"], self.local_user.remote_id)
self.assertEqual(
activity["cc"][0], "https://www.w3.org/ns/activitystreams#Public"
)
self.local_user.refresh_from_db()
self.assertFalse(self.local_user.is_active)
self.assertEqual(self.local_user.deactivation_reason, "self_deletion")

View file

@ -283,6 +283,46 @@ class BookViews(TestCase):
self.assertEqual(book.authors.first().name, "Sappho")
self.assertEqual(book.authors.first(), book.parent_work.authors.first())
def _setup_cover_url(self):
cover_url = "http://example.com"
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
responses.add(
responses.GET,
cover_url,
body=output.getvalue(),
status=200,
)
return cover_url
@responses.activate
def test_create_book_upload_cover_url(self):
"""create an entirely new book and work with cover url"""
self.assertFalse(self.book.cover)
view = views.ConfirmEditBook.as_view()
self.local_user.groups.add(self.group)
cover_url = self._setup_cover_url()
form = forms.EditionForm()
form.data["title"] = "New Title"
form.data["last_edited_by"] = self.local_user.id
form.data["cover-url"] = cover_url
request = self.factory.post("", form.data)
request.user = self.local_user
with patch(
"bookwyrm.models.activitypub_mixin.broadcast_task.delay"
) as delay_mock:
views.upload_cover(request, self.book.id)
self.assertEqual(delay_mock.call_count, 1)
self.book.refresh_from_db()
self.assertTrue(self.book.cover)
def test_upload_cover_file(self):
"""add a cover via file upload"""
self.assertFalse(self.book.cover)
@ -311,21 +351,8 @@ class BookViews(TestCase):
def test_upload_cover_url(self):
"""add a cover via url"""
self.assertFalse(self.book.cover)
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
responses.add(
responses.GET,
"http://example.com",
body=output.getvalue(),
status=200,
)
form = forms.CoverForm(instance=self.book)
form.data["cover-url"] = "http://example.com"
form.data["cover-url"] = self._setup_cover_url()
request = self.factory.post("", form.data)
request.user = self.local_user

View file

@ -1,5 +1,6 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.contrib.auth.models import AnonymousUser
from django.template.response import TemplateResponse
@ -51,7 +52,16 @@ class DirectoryViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_directory_page_empty(self):
@ -62,7 +72,10 @@ class DirectoryViews(TestCase):
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(html.content, options={"drop-empty-elements": False})
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
def test_directory_page_logged_out(self):

View file

@ -95,33 +95,3 @@ class PasswordViews(TestCase):
resp = view(request, code.code)
resp.render()
self.assertTrue(models.PasswordReset.objects.exists())
def test_password_change_get(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.ChangePassword.as_view()
request = self.factory.get("")
request.user = self.local_user
result = view(request)
self.assertIsInstance(result, TemplateResponse)
result.render()
self.assertEqual(result.status_code, 200)
def test_password_change(self):
"""change password"""
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post("", {"password": "hi", "confirm-password": "hi"})
request.user = self.local_user
with patch("bookwyrm.views.password.login"):
view(request)
self.assertNotEqual(self.local_user.password, password_hash)
def test_password_change_mismatch(self):
"""change password"""
view = views.ChangePassword.as_view()
password_hash = self.local_user.password
request = self.factory.post("", {"password": "hi", "confirm-password": "hihi"})
request.user = self.local_user
view(request)
self.assertEqual(self.local_user.password, password_hash)

View file

@ -1,11 +1,14 @@
""" test for app action functionality """
import json
from unittest.mock import patch
from tidylib import tidy_document
from django.core.exceptions import PermissionDenied
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import models, views
from bookwyrm import forms, models, views
from bookwyrm.activitypub import ActivitypubResponse
@ -53,7 +56,16 @@ class ShelfViews(TestCase):
is_api.return_value = False
result = view(request, self.local_user.username, shelf.identifier)
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
with patch("bookwyrm.views.shelf.is_api_request") as is_api:
@ -122,7 +134,7 @@ class ShelfViews(TestCase):
self.assertEqual(shelf.name, "To Read")
def test_handle_shelve(self, *_):
def test_shelve(self, *_):
"""shelve a book"""
request = self.factory.post(
"", {"book": self.book.id, "shelf": self.shelf.identifier}
@ -140,7 +152,7 @@ class ShelfViews(TestCase):
# make sure the book is on the shelf
self.assertEqual(self.shelf.books.get(), self.book)
def test_handle_shelve_to_read(self, *_):
def test_shelve_to_read(self, *_):
"""special behavior for the to-read shelf"""
shelf = models.Shelf.objects.get(identifier="to-read")
request = self.factory.post(
@ -153,7 +165,7 @@ class ShelfViews(TestCase):
# make sure the book is on the shelf
self.assertEqual(shelf.books.get(), self.book)
def test_handle_shelve_reading(self, *_):
def test_shelve_reading(self, *_):
"""special behavior for the reading shelf"""
shelf = models.Shelf.objects.get(identifier="reading")
request = self.factory.post(
@ -166,7 +178,7 @@ class ShelfViews(TestCase):
# make sure the book is on the shelf
self.assertEqual(shelf.books.get(), self.book)
def test_handle_shelve_read(self, *_):
def test_shelve_read(self, *_):
"""special behavior for the read shelf"""
shelf = models.Shelf.objects.get(identifier="read")
request = self.factory.post(
@ -179,7 +191,7 @@ class ShelfViews(TestCase):
# make sure the book is on the shelf
self.assertEqual(shelf.books.get(), self.book)
def test_handle_unshelve(self, *_):
def test_unshelve(self, *_):
"""remove a book from a shelf"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
models.ShelfBook.objects.create(
@ -197,3 +209,76 @@ class ShelfViews(TestCase):
self.assertEqual(activity["type"], "Remove")
self.assertEqual(activity["object"]["id"], item.remote_id)
self.assertEqual(self.shelf.books.count(), 0)
def test_create_shelf(self, *_):
"""a brand new custom shelf"""
form = forms.ShelfForm()
form.data["user"] = self.local_user.id
form.data["name"] = "new shelf name"
form.data["description"] = "desc"
form.data["privacy"] = "unlisted"
request = self.factory.post("", form.data)
request.user = self.local_user
views.create_shelf(request)
shelf = models.Shelf.objects.get(name="new shelf name")
self.assertEqual(shelf.privacy, "unlisted")
self.assertEqual(shelf.description, "desc")
self.assertEqual(shelf.user, self.local_user)
def test_delete_shelf(self, *_):
"""delete a brand new custom shelf"""
request = self.factory.post("")
request.user = self.local_user
shelf_id = self.shelf.id
views.delete_shelf(request, shelf_id)
self.assertFalse(models.Shelf.objects.filter(id=shelf_id).exists())
def test_delete_shelf_unauthorized(self, *_):
"""delete a brand new custom shelf"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
rat = models.User.objects.create_user(
"rat@local.com",
"rat@mouse.mouse",
"password",
local=True,
localname="rat",
)
request = self.factory.post("")
request.user = rat
with self.assertRaises(PermissionDenied):
views.delete_shelf(request, self.shelf.id)
self.assertTrue(models.Shelf.objects.filter(id=self.shelf.id).exists())
def test_delete_shelf_has_book(self, *_):
"""delete a brand new custom shelf"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
models.ShelfBook.objects.create(
book=self.book, user=self.local_user, shelf=self.shelf
)
request = self.factory.post("")
request.user = self.local_user
with self.assertRaises(PermissionDenied):
views.delete_shelf(request, self.shelf.id)
self.assertTrue(models.Shelf.objects.filter(id=self.shelf.id).exists())
def test_delete_shelf_not_editable(self, *_):
"""delete a brand new custom shelf"""
shelf = self.local_user.shelf_set.first()
self.assertFalse(shelf.editable)
request = self.factory.post("")
request.user = self.local_user
with self.assertRaises(PermissionDenied):
views.delete_shelf(request, shelf.id)
self.assertTrue(models.Shelf.objects.filter(id=shelf.id).exists())

View file

@ -1,5 +1,6 @@
""" test for app action functionality """
from unittest.mock import patch
from tidylib import tidy_document
from django.contrib.auth.models import AnonymousUser
from django.http.response import Http404
@ -55,7 +56,16 @@ class UserViews(TestCase):
is_api.return_value = False
result = view(request, "mouse")
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
request.user = self.anonymous_user
@ -63,7 +73,16 @@ class UserViews(TestCase):
is_api.return_value = False
result = view(request, "mouse")
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
with patch("bookwyrm.views.user.is_api_request") as is_api:
@ -92,7 +111,16 @@ class UserViews(TestCase):
is_api.return_value = False
result = view(request, "mouse")
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
with patch("bookwyrm.views.user.is_api_request") as is_api:
@ -123,7 +151,16 @@ class UserViews(TestCase):
is_api.return_value = False
result = view(request, "mouse")
self.assertIsInstance(result, TemplateResponse)
result.render()
html = result.render()
_, errors = tidy_document(
html.content,
options={
"drop-empty-elements": False,
"warn-proprietary-attributes": False,
},
)
if errors:
raise Exception(errors)
self.assertEqual(result.status_code, 200)
with patch("bookwyrm.views.user.is_api_request") as is_api:

View file

@ -1,4 +1,5 @@
""" make sure all our nice views are available """
# site admin
from .admin.announcements import Announcements, Announcement, delete_announcement
from .admin.dashboard import Dashboard
from .admin.federation import Federation, FederatedServer
@ -19,13 +20,19 @@ from .admin.reports import (
)
from .admin.site import Site
from .admin.user_admin import UserAdmin, UserAdminList
# user preferences
from .preferences.change_password import ChangePassword
from .preferences.edit_user import EditUser
from .preferences.delete_user import DeleteUser
from .preferences.block import Block, unblock
# misc views
from .author import Author, EditAuthor
from .block import Block, unblock
from .books import Book, EditBook, ConfirmEditBook
from .books import upload_cover, add_description, resolve_book
from .directory import Directory
from .discover import Discover
from .edit_user import EditUser, DeleteUser
from .editions import Editions, switch_edition
from .feed import DirectMessage, Feed, Replies, Status
from .follow import follow, unfollow
@ -47,7 +54,7 @@ from .reading import delete_readthrough, delete_progressupdate
from .reading import ReadingStatus
from .register import Register, ConfirmEmail, ConfirmEmailCode, resend_link
from .rss_feed import RssFeed
from .password import PasswordResetRequest, PasswordReset, ChangePassword
from .password import PasswordResetRequest, PasswordReset
from .search import Search
from .shelf import Shelf
from .shelf import create_shelf, delete_shelf

View file

@ -41,7 +41,9 @@ class Announcements(View):
"form": forms.AnnouncementForm(),
"sort": sort,
}
return TemplateResponse(request, "settings/announcements.html", data)
return TemplateResponse(
request, "settings/announcements/announcements.html", data
)
def post(self, request):
"""edit the site settings"""
@ -56,7 +58,9 @@ class Announcements(View):
).get_page(request.GET.get("page")),
"form": form,
}
return TemplateResponse(request, "settings/announcements.html", data)
return TemplateResponse(
request, "settings/announcements/announcements.html", data
)
@method_decorator(login_required, name="dispatch")
@ -74,7 +78,9 @@ class Announcement(View):
"announcement": announcement,
"form": forms.AnnouncementForm(instance=announcement),
}
return TemplateResponse(request, "settings/announcement.html", data)
return TemplateResponse(
request, "settings/announcements/announcement.html", data
)
def post(self, request, announcement_id):
"""edit announcement"""
@ -87,7 +93,9 @@ class Announcement(View):
"announcement": announcement,
"form": form,
}
return TemplateResponse(request, "settings/announcement.html", data)
return TemplateResponse(
request, "settings/announcements/announcement.html", data
)
@login_required

View file

@ -85,4 +85,4 @@ class Dashboard(View):
"user_stats": user_stats,
"status_stats": status_stats,
}
return TemplateResponse(request, "settings/dashboard.html", data)
return TemplateResponse(request, "settings/dashboard/dashboard.html", data)

View file

@ -22,7 +22,9 @@ class EmailBlocklist(View):
"domains": models.EmailBlocklist.objects.order_by("-created_date").all(),
"form": forms.EmailBlocklistForm(),
}
return TemplateResponse(request, "settings/email_blocklist.html", data)
return TemplateResponse(
request, "settings/email_blocklist/email_blocklist.html", data
)
def post(self, request, domain_id=None):
"""create a new domain block"""
@ -35,11 +37,15 @@ class EmailBlocklist(View):
"form": form,
}
if not form.is_valid():
return TemplateResponse(request, "settings/email_blocklist.html", data)
return TemplateResponse(
request, "settings/email_blocklist/email_blocklist.html", data
)
form.save()
data["form"] = forms.EmailBlocklistForm()
return TemplateResponse(request, "settings/email_blocklist.html", data)
return TemplateResponse(
request, "settings/email_blocklist/email_blocklist.html", data
)
# pylint: disable=unused-argument
def delete(self, request, domain_id):

View file

@ -44,7 +44,7 @@ class Federation(View):
"sort": sort,
"form": forms.ServerForm(),
}
return TemplateResponse(request, "settings/federation.html", data)
return TemplateResponse(request, "settings/federation/instance_list.html", data)
class AddFederatedServer(View):
@ -53,14 +53,16 @@ class AddFederatedServer(View):
def get(self, request):
"""add server form"""
data = {"form": forms.ServerForm()}
return TemplateResponse(request, "settings/edit_server.html", data)
return TemplateResponse(request, "settings/federation/edit_instance.html", data)
def post(self, request):
"""add a server from the admin panel"""
form = forms.ServerForm(request.POST)
if not form.is_valid():
data = {"form": form}
return TemplateResponse(request, "settings/edit_server.html", data)
return TemplateResponse(
request, "settings/federation/edit_instance.html", data
)
server = form.save()
return redirect("settings-federated-server", server.id)
@ -75,7 +77,7 @@ class ImportServerBlocklist(View):
def get(self, request):
"""add server form"""
return TemplateResponse(request, "settings/server_blocklist.html")
return TemplateResponse(request, "settings/federation/instance_blocklist.html")
def post(self, request):
"""add a server from the admin panel"""
@ -98,7 +100,9 @@ class ImportServerBlocklist(View):
server.block()
success_count += 1
data = {"failed": failed, "succeeded": success_count}
return TemplateResponse(request, "settings/server_blocklist.html", data)
return TemplateResponse(
request, "settings/federation/instance_blocklist.html", data
)
@method_decorator(login_required, name="dispatch")
@ -123,7 +127,7 @@ class FederatedServer(View):
user_subject__in=users.all()
),
}
return TemplateResponse(request, "settings/federated_server.html", data)
return TemplateResponse(request, "settings/federation/instance.html", data)
def post(self, request, server): # pylint: disable=unused-argument
"""update note"""

View file

@ -45,7 +45,7 @@ class ManageInvites(View):
),
"form": forms.CreateInviteForm(),
}
return TemplateResponse(request, "settings/manage_invites.html", data)
return TemplateResponse(request, "settings/invites/manage_invites.html", data)
def post(self, request):
"""creates an invite database entry"""
@ -64,7 +64,7 @@ class ManageInvites(View):
PAGE_LENGTH,
)
data = {"invites": paginated.page(1), "form": form}
return TemplateResponse(request, "settings/manage_invites.html", data)
return TemplateResponse(request, "settings/invites/manage_invites.html", data)
class Invite(View):
@ -135,7 +135,9 @@ class ManageInviteRequests(View):
),
"sort": sort,
}
return TemplateResponse(request, "settings/manage_invite_requests.html", data)
return TemplateResponse(
request, "settings/invites/manage_invite_requests.html", data
)
def post(self, request):
"""send out an invite"""

View file

@ -22,7 +22,9 @@ class IPBlocklist(View):
"addresses": models.IPBlocklist.objects.all(),
"form": forms.IPBlocklistForm(),
}
return TemplateResponse(request, "settings/ip_blocklist.html", data)
return TemplateResponse(
request, "settings/ip_blocklist/ip_blocklist.html", data
)
def post(self, request, block_id=None):
"""create a new ip address block"""
@ -35,11 +37,15 @@ class IPBlocklist(View):
"form": form,
}
if not form.is_valid():
return TemplateResponse(request, "settings/ip_blocklist.html", data)
return TemplateResponse(
request, "settings/ip_blocklist/ip_blocklist.html", data
)
form.save()
data["form"] = forms.IPBlocklistForm()
return TemplateResponse(request, "settings/ip_blocklist.html", data)
return TemplateResponse(
request, "settings/ip_blocklist/ip_blocklist.html", data
)
# pylint: disable=unused-argument
def delete(self, request, domain_id):

View file

@ -40,7 +40,7 @@ class Reports(View):
"server": server,
"reports": models.Report.objects.filter(**filters),
}
return TemplateResponse(request, "moderation/reports.html", data)
return TemplateResponse(request, "settings/reports/reports.html", data)
@method_decorator(login_required, name="dispatch")
@ -60,7 +60,7 @@ class Report(View):
data = {
"report": get_object_or_404(models.Report, id=report_id),
}
return TemplateResponse(request, "moderation/report.html", data)
return TemplateResponse(request, "settings/reports/report.html", data)
def post(self, request, report_id):
"""comment on a report"""

View file

@ -57,7 +57,7 @@ class UserAdminList(View):
"sort": sort,
"server": server,
}
return TemplateResponse(request, "user_admin/user_admin.html", data)
return TemplateResponse(request, "settings/users/user_admin.html", data)
@method_decorator(login_required, name="dispatch")
@ -72,7 +72,7 @@ class UserAdmin(View):
"""user view"""
user = get_object_or_404(models.User, id=user)
data = {"user": user, "group_form": forms.UserGroupForm()}
return TemplateResponse(request, "user_admin/user.html", data)
return TemplateResponse(request, "settings/users/user.html", data)
def post(self, request, user):
"""update user group"""
@ -81,4 +81,4 @@ class UserAdmin(View):
if form.is_valid():
form.save()
data = {"user": user, "group_form": form}
return TemplateResponse(request, "user_admin/user.html", data)
return TemplateResponse(request, "settings/users/user.html", data)

View file

@ -191,6 +191,8 @@ class EditBook(View):
data["confirm_mode"] = True
# this isn't preserved because it isn't part of the form obj
data["remove_authors"] = request.POST.getlist("remove_authors")
data["cover_url"] = request.POST.get("cover-url")
# make sure the dates are passed in as datetime, they're currently a string
# QueryDicts are immutable, we need to copy
formcopy = data["form"].data.copy()
@ -267,12 +269,20 @@ class ConfirmEditBook(View):
work = models.Work.objects.create(title=form.cleaned_data["title"])
work.authors.set(book.authors.all())
book.parent_work = work
# we don't tell the world when creating a book
book.save(broadcast=False)
for author_id in request.POST.getlist("remove_authors"):
book.authors.remove(author_id)
# import cover, if requested
url = request.POST.get("cover-url")
if url:
image = set_cover_from_url(url)
if image:
book.cover.save(*image, save=False)
# we don't tell the world when creating a book
book.save(broadcast=False)
return redirect(f"/book/{book.id}")

View file

@ -25,10 +25,10 @@ class Directory(View):
users = suggested_users.get_annotated_users(request.user, **filters)
sort = request.GET.get("sort")
if sort == "recent":
users = users.order_by("-last_active_date")
else:
if sort == "suggested":
users = users.order_by("-mutuals", "-last_active_date")
else:
users = users.order_by("-last_active_date")
paginated = Paginator(users, 12)

View file

@ -13,7 +13,7 @@ from django.views import View
from bookwyrm import forms, models
from bookwyrm.connectors import connector_manager
from bookwyrm.suggested_users import suggested_users
from .edit_user import save_user_form
from .preferences.edit_user import save_user_form
# pylint: disable= no-self-use

View file

@ -41,7 +41,7 @@ class Goal(View):
"year": year,
"is_self": request.user == user,
}
return TemplateResponse(request, "goal.html", data)
return TemplateResponse(request, "user/goal.html", data)
def post(self, request, username, year):
"""update or create an annual goal"""
@ -58,7 +58,7 @@ class Goal(View):
"goal": goal,
"year": year,
}
return TemplateResponse(request, "goal.html", data)
return TemplateResponse(request, "user/goal.html", data)
goal = form.save()
if request.POST.get("post-status"):

View file

@ -1,10 +1,8 @@
""" class views for password management """
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views import View
@ -82,26 +80,3 @@ class PasswordReset(View):
login(request, user)
reset_code.delete()
return redirect("/")
@method_decorator(login_required, name="dispatch")
class ChangePassword(View):
"""change password as logged in user"""
def get(self, request):
"""change password page"""
data = {"user": request.user}
return TemplateResponse(request, "preferences/change_password.html", data)
def post(self, request):
"""allow a user to change their password"""
new_password = request.POST.get("password")
confirm_password = request.POST.get("confirm-password")
if new_password != confirm_password:
return redirect("prefs-password")
request.user.set_password(new_password)
request.user.save(broadcast=False, update_fields=["password"])
login(request, request.user)
return redirect("user-feed", request.user.localname)

View file

View file

@ -0,0 +1,31 @@
""" class views for password management """
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
# pylint: disable= no-self-use
@method_decorator(login_required, name="dispatch")
class ChangePassword(View):
"""change password as logged in user"""
def get(self, request):
"""change password page"""
data = {"user": request.user}
return TemplateResponse(request, "preferences/change_password.html", data)
def post(self, request):
"""allow a user to change their password"""
new_password = request.POST.get("password")
confirm_password = request.POST.get("confirm-password")
if new_password != confirm_password:
return redirect("prefs-password")
request.user.set_password(new_password)
request.user.save(broadcast=False, update_fields=["password"])
login(request, request.user)
return redirect("user-feed", request.user.localname)

View file

@ -0,0 +1,38 @@
""" edit your own account """
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
from bookwyrm import forms, models
# pylint: disable=no-self-use
@method_decorator(login_required, name="dispatch")
class DeleteUser(View):
"""delete user view"""
def get(self, request):
"""delete page for a user"""
data = {
"form": forms.DeleteUserForm(),
"user": request.user,
}
return TemplateResponse(request, "preferences/delete_user.html", data)
def post(self, request):
"""les get fancy with images"""
form = forms.DeleteUserForm(request.POST, instance=request.user)
# idk why but I couldn't get check_password to work on request.user
user = models.User.objects.get(id=request.user.id)
if form.is_valid() and user.check_password(form.cleaned_data["password"]):
user.deactivation_reason = "self_deletion"
user.delete()
logout(request)
return redirect("/")
form.errors["password"] = ["Invalid password"]
data = {"form": form, "user": request.user}
return TemplateResponse(request, "preferences/delete_user.html", data)

View file

@ -1,9 +1,8 @@
""" edit or delete ones own account"""
""" edit your own account """
from io import BytesIO
from uuid import uuid4
from PIL import Image
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
from django.core.files.base import ContentFile
from django.shortcuts import redirect
@ -11,7 +10,7 @@ from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
from django.views import View
from bookwyrm import forms, models
from bookwyrm import forms
# pylint: disable=no-self-use
@ -39,35 +38,6 @@ class EditUser(View):
return redirect("user-feed", request.user.localname)
# pylint: disable=no-self-use
@method_decorator(login_required, name="dispatch")
class DeleteUser(View):
"""delete user view"""
def get(self, request):
"""delete page for a user"""
data = {
"form": forms.DeleteUserForm(),
"user": request.user,
}
return TemplateResponse(request, "preferences/delete_user.html", data)
def post(self, request):
"""les get fancy with images"""
form = forms.DeleteUserForm(request.POST, instance=request.user)
# idk why but I couldn't get check_password to work on request.user
user = models.User.objects.get(id=request.user.id)
if form.is_valid() and user.check_password(form.cleaned_data["password"]):
user.deactivation_reason = "self_deletion"
user.delete()
logout(request)
return redirect("/")
form.errors["password"] = ["Invalid password"]
data = {"form": form, "user": request.user}
return TemplateResponse(request, "preferences/delete_user.html", data)
def save_user_form(form):
"""special handling for the user form"""
user = form.save(commit=False)

View file

@ -85,12 +85,14 @@ class Shelf(View):
"shelves": shelves,
"shelf": shelf,
"books": page,
"edit_form": forms.ShelfForm(instance=shelf if shelf_identifier else None),
"create_form": forms.ShelfForm(),
"page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1
),
}
return TemplateResponse(request, "user/shelf/shelf.html", data)
return TemplateResponse(request, "shelf/shelf.html", data)
@method_decorator(login_required, name="dispatch")
# pylint: disable=unused-argument
@ -128,7 +130,7 @@ def create_shelf(request):
def delete_shelf(request, shelf_id):
"""user generated shelves"""
shelf = get_object_or_404(models.Shelf, id=shelf_id)
shelf.raise_not_deletable()
shelf.raise_not_deletable(request.user)
shelf.delete()
return redirect("user-shelves", request.user.localname)

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-20 19:44+0000\n"
"POT-Creation-Date: 2021-09-28 18:31+0000\n"
"PO-Revision-Date: 2021-03-02 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n"
@ -18,67 +18,68 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: bookwyrm/forms.py:242
#: bookwyrm/forms.py:241
#, fuzzy
#| msgid "A user with that username already exists."
msgid "A user with this email already exists."
msgstr "Dieser Benutzename ist bereits vergeben."
#: bookwyrm/forms.py:256
#: bookwyrm/forms.py:255
msgid "One Day"
msgstr "Ein Tag"
#: bookwyrm/forms.py:257
#: bookwyrm/forms.py:256
msgid "One Week"
msgstr "Eine Woche"
#: bookwyrm/forms.py:258
#: bookwyrm/forms.py:257
msgid "One Month"
msgstr "Ein Monat"
#: bookwyrm/forms.py:259
#: bookwyrm/forms.py:258
msgid "Does Not Expire"
msgstr "Läuft nicht aus"
#: bookwyrm/forms.py:264
#, python-format
msgid "%(count)d uses"
msgstr "%(count)d Benutzungen"
#: bookwyrm/forms.py:262
#, fuzzy, python-brace-format
#| msgid "Max uses"
msgid "{i} uses"
msgstr "Maximale Benutzungen"
#: bookwyrm/forms.py:267
#: bookwyrm/forms.py:263
#, fuzzy
#| msgid "Unlisted"
msgid "Unlimited"
msgstr "Ungelistet"
#: bookwyrm/forms.py:329
#: bookwyrm/forms.py:325
msgid "List Order"
msgstr "Reihenfolge der Liste"
#: bookwyrm/forms.py:330
#: bookwyrm/forms.py:326
#, fuzzy
#| msgid "Title"
msgid "Book Title"
msgstr "Titel"
#: bookwyrm/forms.py:331
#: bookwyrm/forms.py:327
#: bookwyrm/templates/snippets/create_status/review.html:33
#: bookwyrm/templates/user/shelf/shelf.html:117
#: bookwyrm/templates/user/shelf/shelf.html:148
msgid "Rating"
msgstr ""
#: bookwyrm/forms.py:333 bookwyrm/templates/lists/list.html:107
#: bookwyrm/forms.py:329 bookwyrm/templates/lists/list.html:109
msgid "Sort By"
msgstr "Sortieren nach"
#: bookwyrm/forms.py:337
#: bookwyrm/forms.py:333
#, fuzzy
#| msgid "Started reading"
msgid "Ascending"
msgstr "Zu lesen angefangen"
#: bookwyrm/forms.py:338
#: bookwyrm/forms.py:334
#, fuzzy
#| msgid "Started reading"
msgid "Descending"
@ -92,29 +93,29 @@ msgstr ""
msgid "Could not find a match for book"
msgstr ""
#: bookwyrm/models/base_model.py:13
#: bookwyrm/models/base_model.py:16
#, fuzzy
#| msgid "Started reading"
msgid "Pending"
msgstr "Zu lesen angefangen"
#: bookwyrm/models/base_model.py:14
#: bookwyrm/models/base_model.py:17
msgid "Self deletion"
msgstr ""
#: bookwyrm/models/base_model.py:15
#: bookwyrm/models/base_model.py:18
#, fuzzy
#| msgid "Moderator Comments"
msgid "Moderator suspension"
msgstr "Moderator:innenkommentare"
#: bookwyrm/models/base_model.py:16
#: bookwyrm/models/base_model.py:19
#, fuzzy
#| msgid "List curation:"
msgid "Moderator deletion"
msgstr "Listenkuratierung:"
#: bookwyrm/models/base_model.py:17
#: bookwyrm/models/base_model.py:20
msgid "Domain block"
msgstr ""
@ -143,7 +144,7 @@ msgstr "%(value)s ist keine gültige remote_id"
msgid "%(value)s is not a valid username"
msgstr "%(value)s ist kein gültiger Username"
#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:169
#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171
msgid "username"
msgstr "Username"
@ -353,7 +354,7 @@ msgstr ""
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:80
#: bookwyrm/templates/preferences/edit_user.html:118
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
#: bookwyrm/templates/settings/federated_server.html:98
@ -483,7 +484,7 @@ msgstr "Themen"
msgid "Places"
msgstr "Orte"
#: bookwyrm/templates/book/book.html:292 bookwyrm/templates/layout.html:73
#: bookwyrm/templates/book/book.html:292 bookwyrm/templates/layout.html:75
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search/layout.html:25
#: bookwyrm/templates/search/layout.html:50
@ -499,7 +500,7 @@ msgstr "Zur Liste"
#: bookwyrm/templates/book/book.html:313
#: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:179
#: bookwyrm/templates/lists/list.html:181
#: bookwyrm/templates/settings/domain_form.html:26
#: bookwyrm/templates/settings/ip_address_form.html:32
msgid "Add"
@ -847,7 +848,7 @@ msgstr "Bestätigungslink erneut senden"
#: bookwyrm/templates/confirm_email/resend_form.html:11
#: bookwyrm/templates/landing/layout.html:67
#: bookwyrm/templates/password_reset_request.html:18
#: bookwyrm/templates/preferences/edit_user.html:38
#: bookwyrm/templates/preferences/edit_user.html:56
#: bookwyrm/templates/snippets/register_form.html:13
msgid "Email address:"
msgstr "E-Mail Adresse"
@ -876,7 +877,7 @@ msgstr "Föderiert"
#: bookwyrm/templates/directory/directory.html:4
#: bookwyrm/templates/directory/directory.html:9
#: bookwyrm/templates/layout.html:99
#: bookwyrm/templates/layout.html:101
msgid "Directory"
msgstr "Vereichnis"
@ -959,7 +960,7 @@ msgstr "Alle bekannten Nutzer*innen"
#: bookwyrm/templates/discover/discover.html:4
#: bookwyrm/templates/discover/discover.html:10
#: bookwyrm/templates/layout.html:76
#: bookwyrm/templates/layout.html:78
#, fuzzy
#| msgid "Discard"
msgid "Discover"
@ -1095,7 +1096,7 @@ msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>"
msgstr "Direktnachrichten mit <a href=\"%(path)s\">%(username)s</a>"
#: bookwyrm/templates/feed/direct_messages.html:10
#: bookwyrm/templates/layout.html:109
#: bookwyrm/templates/layout.html:111
msgid "Direct Messages"
msgstr "Direktnachrichten"
@ -1182,7 +1183,7 @@ msgid "What are you reading?"
msgstr "Zu lesen angefangen"
#: bookwyrm/templates/get_started/books.html:9
#: bookwyrm/templates/layout.html:43 bookwyrm/templates/lists/list.html:135
#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137
msgid "Search for a book"
msgstr "Nach einem Buch suchen"
@ -1200,8 +1201,8 @@ msgstr "Du kannst Bücher hinzufügen, wenn du %(site_name)s benutzt."
#: bookwyrm/templates/get_started/books.html:17
#: bookwyrm/templates/get_started/users.html:18
#: bookwyrm/templates/get_started/users.html:19
#: bookwyrm/templates/layout.html:49 bookwyrm/templates/layout.html:50
#: bookwyrm/templates/lists/list.html:139
#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52
#: bookwyrm/templates/lists/list.html:141
#: bookwyrm/templates/search/layout.html:4
#: bookwyrm/templates/search/layout.html:9
msgid "Search"
@ -1220,7 +1221,7 @@ msgid "Popular on %(site_name)s"
msgstr "Über %(site_name)s"
#: bookwyrm/templates/get_started/books.html:58
#: bookwyrm/templates/lists/list.html:152
#: bookwyrm/templates/lists/list.html:154
msgid "No books found"
msgstr "Keine Bücher gefunden"
@ -1274,12 +1275,12 @@ msgid "Finish"
msgstr "Abgeschlossen"
#: bookwyrm/templates/get_started/profile.html:15
#: bookwyrm/templates/preferences/edit_user.html:24
#: bookwyrm/templates/preferences/edit_user.html:42
msgid "Display name:"
msgstr "Displayname:"
#: bookwyrm/templates/get_started/profile.html:22
#: bookwyrm/templates/preferences/edit_user.html:31
#: bookwyrm/templates/preferences/edit_user.html:49
msgid "Summary:"
msgstr "Bio:"
@ -1288,17 +1289,17 @@ msgid "A little bit about you"
msgstr "Etwas über dich"
#: bookwyrm/templates/get_started/profile.html:32
#: bookwyrm/templates/preferences/edit_user.html:17
#: bookwyrm/templates/preferences/edit_user.html:27
msgid "Avatar:"
msgstr ""
#: bookwyrm/templates/get_started/profile.html:42
#: bookwyrm/templates/preferences/edit_user.html:62
#: bookwyrm/templates/preferences/edit_user.html:104
msgid "Manually approve followers:"
msgstr "Folgende manuell bestätigen"
#: bookwyrm/templates/get_started/profile.html:48
#: bookwyrm/templates/preferences/edit_user.html:54
#: bookwyrm/templates/preferences/edit_user.html:80
msgid "Show this account in suggested users:"
msgstr "Diesen Account in vorgeschlagenen Usern zeigen"
@ -1538,31 +1539,37 @@ msgstr "Danke! Deine Anfrage ist eingegangen."
msgid "Your Account"
msgstr "Dein Account"
#: bookwyrm/templates/layout.html:41
#: bookwyrm/templates/layout.html:13
#, fuzzy, python-format
#| msgid "About %(site_name)s"
msgid "%(site_name)s search"
msgstr "Über %(site_name)s"
#: bookwyrm/templates/layout.html:43
#, fuzzy
#| msgid "Search for a book or user"
msgid "Search for a book, user, or list"
msgstr "Suche nach Buch oder Benutzer*in"
#: bookwyrm/templates/layout.html:59 bookwyrm/templates/layout.html:60
#: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62
msgid "Main navigation menu"
msgstr "Navigationshauptmenü"
#: bookwyrm/templates/layout.html:70
#: bookwyrm/templates/layout.html:72
msgid "Feed"
msgstr ""
#: bookwyrm/templates/layout.html:104
#: bookwyrm/templates/layout.html:106
#, fuzzy
#| msgid "Your books"
msgid "Your Books"
msgstr "Deine Bücher"
#: bookwyrm/templates/layout.html:114
#: bookwyrm/templates/layout.html:116
msgid "Settings"
msgstr "Einstellungen"
#: bookwyrm/templates/layout.html:123
#: bookwyrm/templates/layout.html:125
#: bookwyrm/templates/settings/layout.html:40
#: bookwyrm/templates/settings/manage_invite_requests.html:15
#: bookwyrm/templates/settings/manage_invites.html:3
@ -1570,77 +1577,77 @@ msgstr "Einstellungen"
msgid "Invites"
msgstr "Einladungen"
#: bookwyrm/templates/layout.html:130
#: bookwyrm/templates/layout.html:132
msgid "Admin"
msgstr ""
#: bookwyrm/templates/layout.html:137
#: bookwyrm/templates/layout.html:139
msgid "Log out"
msgstr "Abmelden"
#: bookwyrm/templates/layout.html:145 bookwyrm/templates/layout.html:146
#: bookwyrm/templates/layout.html:147 bookwyrm/templates/layout.html:148
#: bookwyrm/templates/notifications.html:6
#: bookwyrm/templates/notifications.html:11
msgid "Notifications"
msgstr "Benachrichtigungen"
#: bookwyrm/templates/layout.html:168 bookwyrm/templates/layout.html:172
#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174
#: bookwyrm/templates/login.html:21
#: bookwyrm/templates/snippets/register_form.html:4
msgid "Username:"
msgstr ""
#: bookwyrm/templates/layout.html:173
#: bookwyrm/templates/layout.html:175
msgid "password"
msgstr "Passwort"
#: bookwyrm/templates/layout.html:174 bookwyrm/templates/login.html:40
#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40
msgid "Forgot your password?"
msgstr "Passwort vergessen?"
#: bookwyrm/templates/layout.html:177 bookwyrm/templates/login.html:7
#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7
#: bookwyrm/templates/login.html:37
msgid "Log in"
msgstr "Anmelden"
#: bookwyrm/templates/layout.html:185
#: bookwyrm/templates/layout.html:187
msgid "Join"
msgstr ""
#: bookwyrm/templates/layout.html:219
#: bookwyrm/templates/layout.html:221
#, fuzzy
#| msgid "Successfully imported"
msgid "Successfully posted status"
msgstr "Erfolgreich importiert"
#: bookwyrm/templates/layout.html:220
#: bookwyrm/templates/layout.html:222
#, fuzzy
#| msgid "Boost status"
msgid "Error posting status"
msgstr "Status teilen"
#: bookwyrm/templates/layout.html:228
#: bookwyrm/templates/layout.html:230
#, fuzzy
#| msgid "About this server"
msgid "About this instance"
msgstr "Über diesen Server"
#: bookwyrm/templates/layout.html:232
#: bookwyrm/templates/layout.html:234
msgid "Contact site admin"
msgstr "Admin kontaktieren"
#: bookwyrm/templates/layout.html:236
#: bookwyrm/templates/layout.html:238
#, fuzzy
#| msgid "List curation:"
msgid "Documentation"
msgstr "Listenkuratierung:"
#: bookwyrm/templates/layout.html:243
#: bookwyrm/templates/layout.html:245
#, python-format
msgid "Support %(site_name)s on <a href=\"%(support_link)s\" target=\"_blank\">%(support_title)s</a>"
msgstr "%(site_name)s auf <a href=\"%(support_link)s\" target=\"_blank\">%(support_title)s</a> unterstützen"
#: bookwyrm/templates/layout.html:247
#: bookwyrm/templates/layout.html:249
msgid "BookWyrm's source code is freely available. You can contribute or report issues on <a href=\"https://github.com/mouse-reeve/bookwyrm\">GitHub</a>."
msgstr "BookWyrm ist open source Software. Du kannst dich auf <a href=\"https://github.com/mouse-reeve/bookwyrm\">GitHub</a> beteiligen oder etwas melden."
@ -1746,7 +1753,7 @@ msgstr "Offen"
msgid "Anyone can add books to this list"
msgstr "Alle können Bücher hinzufügen"
#: bookwyrm/templates/lists/form.html:49
#: bookwyrm/templates/lists/form.html:50
#, fuzzy
#| msgid "Delete status"
msgid "Delete list"
@ -1772,7 +1779,7 @@ msgstr "Diese Liste ist momentan leer"
msgid "Added by <a href=\"%(user_path)s\">%(username)s</a>"
msgstr "Direktnachrichten mit <a href=\"%(path)s\">%(username)s</a>"
#: bookwyrm/templates/lists/list.html:74
#: bookwyrm/templates/lists/list.html:75
#, fuzzy
#| msgid "List curation:"
msgid "List position"
@ -1784,46 +1791,46 @@ msgstr "Listenkuratierung:"
msgid "Set"
msgstr "Gestartet"
#: bookwyrm/templates/lists/list.html:89
#: bookwyrm/templates/lists/list.html:91
#: bookwyrm/templates/snippets/shelf_selector.html:26
msgid "Remove"
msgstr "Entfernen"
#: bookwyrm/templates/lists/list.html:103
#: bookwyrm/templates/lists/list.html:120
#: bookwyrm/templates/lists/list.html:105
#: bookwyrm/templates/lists/list.html:122
#, fuzzy
#| msgid "Your Lists"
msgid "Sort List"
msgstr "Deine Listen"
#: bookwyrm/templates/lists/list.html:113
#: bookwyrm/templates/lists/list.html:115
#, fuzzy
#| msgid "List curation:"
msgid "Direction"
msgstr "Listenkuratierung:"
#: bookwyrm/templates/lists/list.html:127
#: bookwyrm/templates/lists/list.html:129
msgid "Add Books"
msgstr "Bücher hinzufügen"
#: bookwyrm/templates/lists/list.html:129
#: bookwyrm/templates/lists/list.html:131
msgid "Suggest Books"
msgstr "Bücher vorschlagen"
#: bookwyrm/templates/lists/list.html:140
#: bookwyrm/templates/lists/list.html:142
msgid "search"
msgstr "suchen"
#: bookwyrm/templates/lists/list.html:146
#: bookwyrm/templates/lists/list.html:148
msgid "Clear search"
msgstr "Suche leeren"
#: bookwyrm/templates/lists/list.html:151
#: bookwyrm/templates/lists/list.html:153
#, python-format
msgid "No books found matching the query \"%(query)s\""
msgstr "Keine passenden Bücher zu \"%(query)s\" gefunden"
#: bookwyrm/templates/lists/list.html:179
#: bookwyrm/templates/lists/list.html:181
msgid "Suggest"
msgstr "Vorschlagen"
@ -2112,7 +2119,7 @@ msgstr "Passwort zurücksetzen"
#: bookwyrm/templates/preferences/blocks.html:4
#: bookwyrm/templates/preferences/blocks.html:7
#: bookwyrm/templates/preferences/layout.html:30
#: bookwyrm/templates/preferences/layout.html:31
msgid "Blocked Users"
msgstr "Blockierte Nutzer*innen"
@ -2123,7 +2130,7 @@ msgstr "Momentan keine Nutzer*innen blockiert."
#: bookwyrm/templates/preferences/change_password.html:4
#: bookwyrm/templates/preferences/change_password.html:7
#: bookwyrm/templates/preferences/change_password.html:21
#: bookwyrm/templates/preferences/layout.html:19
#: bookwyrm/templates/preferences/layout.html:20
msgid "Change Password"
msgstr "Passwort ändern"
@ -2134,7 +2141,7 @@ msgstr "Neues Passwort:"
#: bookwyrm/templates/preferences/delete_user.html:4
#: bookwyrm/templates/preferences/delete_user.html:7
#: bookwyrm/templates/preferences/delete_user.html:26
#: bookwyrm/templates/preferences/layout.html:23
#: bookwyrm/templates/preferences/layout.html:24
#: bookwyrm/templates/user_admin/delete_user_form.html:23
#, fuzzy
#| msgid "Create an Account"
@ -2151,46 +2158,62 @@ msgstr "Das Löschen des Accounts kann nicht rückgängig gemacht werden. Der Us
#: bookwyrm/templates/preferences/edit_user.html:4
#: bookwyrm/templates/preferences/edit_user.html:7
#: bookwyrm/templates/preferences/layout.html:15
msgid "Edit Profile"
msgstr "Profil bearbeiten:"
#: bookwyrm/templates/preferences/edit_user.html:46
#: bookwyrm/templates/preferences/edit_user.html:12
#: bookwyrm/templates/preferences/edit_user.html:25
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Profil"
#: bookwyrm/templates/preferences/edit_user.html:13
#: bookwyrm/templates/preferences/edit_user.html:68
#, fuzzy
#| msgid "Email preference"
msgid "Display preferences"
msgstr "E-Mail Einstellungen"
#: bookwyrm/templates/preferences/edit_user.html:14
#: bookwyrm/templates/preferences/edit_user.html:100
#, fuzzy
#| msgid "Private"
msgid "Privacy"
msgstr "Privat"
#: bookwyrm/templates/preferences/edit_user.html:72
#, fuzzy
#| msgid "Show set reading goal prompt in feed:"
msgid "Show reading goal prompt in feed:"
msgstr "Angegebenes Leseziel im Feed anzeigen."
#: bookwyrm/templates/preferences/edit_user.html:50
#: bookwyrm/templates/preferences/edit_user.html:76
#, fuzzy
#| msgid "Suggest Books"
msgid "Show suggested users:"
msgstr "Bücher vorschlagen"
#: bookwyrm/templates/preferences/edit_user.html:58
#: bookwyrm/templates/preferences/edit_user.html:85
#, python-format
msgid "Your account will show up in the <a href=\"%(path)s\">directory</a>, and may be recommended to other BookWyrm users."
msgstr "Dein Account wird im <a href=\"%(path)s\">directory</a> angezeigt und eventuell anderen Usern empfohlen."
#: bookwyrm/templates/preferences/edit_user.html:68
#: bookwyrm/templates/preferences/edit_user.html:89
msgid "Preferred Timezone: "
msgstr "Bevorzugte Zeitzone:"
#: bookwyrm/templates/preferences/edit_user.html:110
#, fuzzy
#| msgid "Goal privacy:"
msgid "Default post privacy:"
msgstr "Sichtbarkeit des Ziels"
#: bookwyrm/templates/preferences/edit_user.html:75
msgid "Preferred Timezone: "
msgstr "Bevorzugte Zeitzone:"
#: bookwyrm/templates/preferences/layout.html:11
msgid "Account"
msgstr ""
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Profil"
#: bookwyrm/templates/preferences/layout.html:26
#: bookwyrm/templates/preferences/layout.html:27
msgid "Relationships"
msgstr "Beziehungen"
@ -3604,7 +3627,7 @@ msgstr "Zu lesen angefangen"
msgid "Show more"
msgstr "Mehr anzeigen"
#: bookwyrm/templates/snippets/trimmed_text.html:34
#: bookwyrm/templates/snippets/trimmed_text.html:35
msgid "Show less"
msgstr "Weniger anzeigen"
@ -3938,7 +3961,7 @@ msgstr ""
msgid "Not a valid csv file"
msgstr "E-Mail Adresse"
#: bookwyrm/views/login.py:70
#: bookwyrm/views/login.py:68
msgid "Username or password are incorrect"
msgstr "Username oder Passwort sind falsch"
@ -3949,8 +3972,9 @@ msgid "No user with that email address was found."
msgstr "Dieser Benutzename ist bereits vergeben."
#: bookwyrm/views/password.py:41
#, python-format
msgid "A password reset link sent to %s"
#, fuzzy, python-brace-format
#| msgid "A password reset link sent to %s"
msgid "A password reset link sent to {email}"
msgstr "Ein Passwortwiederherstellungslinl wurde zu %s gesendet"
#: bookwyrm/views/rss_feed.py:34
@ -3958,6 +3982,9 @@ msgstr "Ein Passwortwiederherstellungslinl wurde zu %s gesendet"
msgid "Status updates from {obj.display_name}"
msgstr "Status updates von {obj.display_name}"
#~ msgid "%(count)d uses"
#~ msgstr "%(count)d Benutzungen"
#~ msgid "This instance is closed"
#~ msgstr "Diese Instanz ist geschlossen"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-20 19:44+0000\n"
"POT-Creation-Date: 2021-09-28 18:31+0000\n"
"PO-Revision-Date: 2021-02-28 17:19-0800\n"
"Last-Translator: Mouse Reeve <mousereeve@riseup.net>\n"
"Language-Team: English <LL@li.org>\n"
@ -18,59 +18,59 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: bookwyrm/forms.py:242
#: bookwyrm/forms.py:241
msgid "A user with this email already exists."
msgstr ""
#: bookwyrm/forms.py:256
#: bookwyrm/forms.py:255
msgid "One Day"
msgstr ""
#: bookwyrm/forms.py:257
#: bookwyrm/forms.py:256
msgid "One Week"
msgstr ""
#: bookwyrm/forms.py:258
#: bookwyrm/forms.py:257
msgid "One Month"
msgstr ""
#: bookwyrm/forms.py:259
#: bookwyrm/forms.py:258
msgid "Does Not Expire"
msgstr ""
#: bookwyrm/forms.py:264
#, python-format
msgid "%(count)d uses"
#: bookwyrm/forms.py:262
#, python-brace-format
msgid "{i} uses"
msgstr ""
#: bookwyrm/forms.py:267
#: bookwyrm/forms.py:263
msgid "Unlimited"
msgstr ""
#: bookwyrm/forms.py:329
#: bookwyrm/forms.py:325
msgid "List Order"
msgstr ""
#: bookwyrm/forms.py:330
#: bookwyrm/forms.py:326
msgid "Book Title"
msgstr ""
#: bookwyrm/forms.py:331
#: bookwyrm/forms.py:327
#: bookwyrm/templates/snippets/create_status/review.html:33
#: bookwyrm/templates/user/shelf/shelf.html:117
#: bookwyrm/templates/user/shelf/shelf.html:148
msgid "Rating"
msgstr ""
#: bookwyrm/forms.py:333 bookwyrm/templates/lists/list.html:107
#: bookwyrm/forms.py:329 bookwyrm/templates/lists/list.html:109
msgid "Sort By"
msgstr ""
#: bookwyrm/forms.py:337
#: bookwyrm/forms.py:333
msgid "Ascending"
msgstr ""
#: bookwyrm/forms.py:338
#: bookwyrm/forms.py:334
msgid "Descending"
msgstr ""
@ -82,23 +82,23 @@ msgstr ""
msgid "Could not find a match for book"
msgstr ""
#: bookwyrm/models/base_model.py:13
#: bookwyrm/models/base_model.py:16
msgid "Pending"
msgstr ""
#: bookwyrm/models/base_model.py:14
#: bookwyrm/models/base_model.py:17
msgid "Self deletion"
msgstr ""
#: bookwyrm/models/base_model.py:15
#: bookwyrm/models/base_model.py:18
msgid "Moderator suspension"
msgstr ""
#: bookwyrm/models/base_model.py:16
#: bookwyrm/models/base_model.py:19
msgid "Moderator deletion"
msgstr ""
#: bookwyrm/models/base_model.py:17
#: bookwyrm/models/base_model.py:20
msgid "Domain block"
msgstr ""
@ -125,7 +125,7 @@ msgstr ""
msgid "%(value)s is not a valid username"
msgstr ""
#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:169
#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171
msgid "username"
msgstr ""
@ -321,7 +321,7 @@ msgstr ""
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:80
#: bookwyrm/templates/preferences/edit_user.html:118
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
#: bookwyrm/templates/settings/federated_server.html:98
@ -438,7 +438,7 @@ msgstr ""
msgid "Places"
msgstr ""
#: bookwyrm/templates/book/book.html:292 bookwyrm/templates/layout.html:73
#: bookwyrm/templates/book/book.html:292 bookwyrm/templates/layout.html:75
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search/layout.html:25
#: bookwyrm/templates/search/layout.html:50
@ -452,7 +452,7 @@ msgstr ""
#: bookwyrm/templates/book/book.html:313
#: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:179
#: bookwyrm/templates/lists/list.html:181
#: bookwyrm/templates/settings/domain_form.html:26
#: bookwyrm/templates/settings/ip_address_form.html:32
msgid "Add"
@ -772,7 +772,7 @@ msgstr ""
#: bookwyrm/templates/confirm_email/resend_form.html:11
#: bookwyrm/templates/landing/layout.html:67
#: bookwyrm/templates/password_reset_request.html:18
#: bookwyrm/templates/preferences/edit_user.html:38
#: bookwyrm/templates/preferences/edit_user.html:56
#: bookwyrm/templates/snippets/register_form.html:13
msgid "Email address:"
msgstr ""
@ -795,7 +795,7 @@ msgstr ""
#: bookwyrm/templates/directory/directory.html:4
#: bookwyrm/templates/directory/directory.html:9
#: bookwyrm/templates/layout.html:99
#: bookwyrm/templates/layout.html:101
msgid "Directory"
msgstr ""
@ -867,7 +867,7 @@ msgstr ""
#: bookwyrm/templates/discover/discover.html:4
#: bookwyrm/templates/discover/discover.html:10
#: bookwyrm/templates/layout.html:76
#: bookwyrm/templates/layout.html:78
msgid "Discover"
msgstr ""
@ -993,7 +993,7 @@ msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>"
msgstr ""
#: bookwyrm/templates/feed/direct_messages.html:10
#: bookwyrm/templates/layout.html:109
#: bookwyrm/templates/layout.html:111
msgid "Direct Messages"
msgstr ""
@ -1073,7 +1073,7 @@ msgid "What are you reading?"
msgstr ""
#: bookwyrm/templates/get_started/books.html:9
#: bookwyrm/templates/layout.html:43 bookwyrm/templates/lists/list.html:135
#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137
msgid "Search for a book"
msgstr ""
@ -1091,8 +1091,8 @@ msgstr ""
#: bookwyrm/templates/get_started/books.html:17
#: bookwyrm/templates/get_started/users.html:18
#: bookwyrm/templates/get_started/users.html:19
#: bookwyrm/templates/layout.html:49 bookwyrm/templates/layout.html:50
#: bookwyrm/templates/lists/list.html:139
#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52
#: bookwyrm/templates/lists/list.html:141
#: bookwyrm/templates/search/layout.html:4
#: bookwyrm/templates/search/layout.html:9
msgid "Search"
@ -1108,7 +1108,7 @@ msgid "Popular on %(site_name)s"
msgstr ""
#: bookwyrm/templates/get_started/books.html:58
#: bookwyrm/templates/lists/list.html:152
#: bookwyrm/templates/lists/list.html:154
msgid "No books found"
msgstr ""
@ -1153,12 +1153,12 @@ msgid "Finish"
msgstr ""
#: bookwyrm/templates/get_started/profile.html:15
#: bookwyrm/templates/preferences/edit_user.html:24
#: bookwyrm/templates/preferences/edit_user.html:42
msgid "Display name:"
msgstr ""
#: bookwyrm/templates/get_started/profile.html:22
#: bookwyrm/templates/preferences/edit_user.html:31
#: bookwyrm/templates/preferences/edit_user.html:49
msgid "Summary:"
msgstr ""
@ -1167,17 +1167,17 @@ msgid "A little bit about you"
msgstr ""
#: bookwyrm/templates/get_started/profile.html:32
#: bookwyrm/templates/preferences/edit_user.html:17
#: bookwyrm/templates/preferences/edit_user.html:27
msgid "Avatar:"
msgstr ""
#: bookwyrm/templates/get_started/profile.html:42
#: bookwyrm/templates/preferences/edit_user.html:62
#: bookwyrm/templates/preferences/edit_user.html:104
msgid "Manually approve followers:"
msgstr ""
#: bookwyrm/templates/get_started/profile.html:48
#: bookwyrm/templates/preferences/edit_user.html:54
#: bookwyrm/templates/preferences/edit_user.html:80
msgid "Show this account in suggested users:"
msgstr ""
@ -1408,27 +1408,32 @@ msgstr ""
msgid "Your Account"
msgstr ""
#: bookwyrm/templates/layout.html:41
#: bookwyrm/templates/layout.html:13
#, python-format
msgid "%(site_name)s search"
msgstr ""
#: bookwyrm/templates/layout.html:43
msgid "Search for a book, user, or list"
msgstr ""
#: bookwyrm/templates/layout.html:59 bookwyrm/templates/layout.html:60
#: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62
msgid "Main navigation menu"
msgstr ""
#: bookwyrm/templates/layout.html:70
#: bookwyrm/templates/layout.html:72
msgid "Feed"
msgstr ""
#: bookwyrm/templates/layout.html:104
#: bookwyrm/templates/layout.html:106
msgid "Your Books"
msgstr ""
#: bookwyrm/templates/layout.html:114
#: bookwyrm/templates/layout.html:116
msgid "Settings"
msgstr ""
#: bookwyrm/templates/layout.html:123
#: bookwyrm/templates/layout.html:125
#: bookwyrm/templates/settings/layout.html:40
#: bookwyrm/templates/settings/manage_invite_requests.html:15
#: bookwyrm/templates/settings/manage_invites.html:3
@ -1436,69 +1441,69 @@ msgstr ""
msgid "Invites"
msgstr ""
#: bookwyrm/templates/layout.html:130
#: bookwyrm/templates/layout.html:132
msgid "Admin"
msgstr ""
#: bookwyrm/templates/layout.html:137
#: bookwyrm/templates/layout.html:139
msgid "Log out"
msgstr ""
#: bookwyrm/templates/layout.html:145 bookwyrm/templates/layout.html:146
#: bookwyrm/templates/layout.html:147 bookwyrm/templates/layout.html:148
#: bookwyrm/templates/notifications.html:6
#: bookwyrm/templates/notifications.html:11
msgid "Notifications"
msgstr ""
#: bookwyrm/templates/layout.html:168 bookwyrm/templates/layout.html:172
#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174
#: bookwyrm/templates/login.html:21
#: bookwyrm/templates/snippets/register_form.html:4
msgid "Username:"
msgstr ""
#: bookwyrm/templates/layout.html:173
#: bookwyrm/templates/layout.html:175
msgid "password"
msgstr ""
#: bookwyrm/templates/layout.html:174 bookwyrm/templates/login.html:40
#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40
msgid "Forgot your password?"
msgstr ""
#: bookwyrm/templates/layout.html:177 bookwyrm/templates/login.html:7
#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7
#: bookwyrm/templates/login.html:37
msgid "Log in"
msgstr ""
#: bookwyrm/templates/layout.html:185
#: bookwyrm/templates/layout.html:187
msgid "Join"
msgstr ""
#: bookwyrm/templates/layout.html:219
#: bookwyrm/templates/layout.html:221
msgid "Successfully posted status"
msgstr ""
#: bookwyrm/templates/layout.html:220
#: bookwyrm/templates/layout.html:222
msgid "Error posting status"
msgstr ""
#: bookwyrm/templates/layout.html:228
#: bookwyrm/templates/layout.html:230
msgid "About this instance"
msgstr ""
#: bookwyrm/templates/layout.html:232
#: bookwyrm/templates/layout.html:234
msgid "Contact site admin"
msgstr ""
#: bookwyrm/templates/layout.html:236
#: bookwyrm/templates/layout.html:238
msgid "Documentation"
msgstr ""
#: bookwyrm/templates/layout.html:243
#: bookwyrm/templates/layout.html:245
#, python-format
msgid "Support %(site_name)s on <a href=\"%(support_link)s\" target=\"_blank\">%(support_title)s</a>"
msgstr ""
#: bookwyrm/templates/layout.html:247
#: bookwyrm/templates/layout.html:249
msgid "BookWyrm's source code is freely available. You can contribute or report issues on <a href=\"https://github.com/mouse-reeve/bookwyrm\">GitHub</a>."
msgstr ""
@ -1598,7 +1603,7 @@ msgstr ""
msgid "Anyone can add books to this list"
msgstr ""
#: bookwyrm/templates/lists/form.html:49
#: bookwyrm/templates/lists/form.html:50
msgid "Delete list"
msgstr ""
@ -1619,7 +1624,7 @@ msgstr ""
msgid "Added by <a href=\"%(user_path)s\">%(username)s</a>"
msgstr ""
#: bookwyrm/templates/lists/list.html:74
#: bookwyrm/templates/lists/list.html:75
msgid "List position"
msgstr ""
@ -1627,42 +1632,42 @@ msgstr ""
msgid "Set"
msgstr ""
#: bookwyrm/templates/lists/list.html:89
#: bookwyrm/templates/lists/list.html:91
#: bookwyrm/templates/snippets/shelf_selector.html:26
msgid "Remove"
msgstr ""
#: bookwyrm/templates/lists/list.html:103
#: bookwyrm/templates/lists/list.html:120
#: bookwyrm/templates/lists/list.html:105
#: bookwyrm/templates/lists/list.html:122
msgid "Sort List"
msgstr ""
#: bookwyrm/templates/lists/list.html:113
#: bookwyrm/templates/lists/list.html:115
msgid "Direction"
msgstr ""
#: bookwyrm/templates/lists/list.html:127
#: bookwyrm/templates/lists/list.html:129
msgid "Add Books"
msgstr ""
#: bookwyrm/templates/lists/list.html:129
#: bookwyrm/templates/lists/list.html:131
msgid "Suggest Books"
msgstr ""
#: bookwyrm/templates/lists/list.html:140
#: bookwyrm/templates/lists/list.html:142
msgid "search"
msgstr ""
#: bookwyrm/templates/lists/list.html:146
#: bookwyrm/templates/lists/list.html:148
msgid "Clear search"
msgstr ""
#: bookwyrm/templates/lists/list.html:151
#: bookwyrm/templates/lists/list.html:153
#, python-format
msgid "No books found matching the query \"%(query)s\""
msgstr ""
#: bookwyrm/templates/lists/list.html:179
#: bookwyrm/templates/lists/list.html:181
msgid "Suggest"
msgstr ""
@ -1926,7 +1931,7 @@ msgstr ""
#: bookwyrm/templates/preferences/blocks.html:4
#: bookwyrm/templates/preferences/blocks.html:7
#: bookwyrm/templates/preferences/layout.html:30
#: bookwyrm/templates/preferences/layout.html:31
msgid "Blocked Users"
msgstr ""
@ -1937,7 +1942,7 @@ msgstr ""
#: bookwyrm/templates/preferences/change_password.html:4
#: bookwyrm/templates/preferences/change_password.html:7
#: bookwyrm/templates/preferences/change_password.html:21
#: bookwyrm/templates/preferences/layout.html:19
#: bookwyrm/templates/preferences/layout.html:20
msgid "Change Password"
msgstr ""
@ -1948,7 +1953,7 @@ msgstr ""
#: bookwyrm/templates/preferences/delete_user.html:4
#: bookwyrm/templates/preferences/delete_user.html:7
#: bookwyrm/templates/preferences/delete_user.html:26
#: bookwyrm/templates/preferences/layout.html:23
#: bookwyrm/templates/preferences/layout.html:24
#: bookwyrm/templates/user_admin/delete_user_form.html:23
msgid "Delete Account"
msgstr ""
@ -1963,40 +1968,52 @@ msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:4
#: bookwyrm/templates/preferences/edit_user.html:7
#: bookwyrm/templates/preferences/layout.html:15
msgid "Edit Profile"
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:46
#: bookwyrm/templates/preferences/edit_user.html:12
#: bookwyrm/templates/preferences/edit_user.html:25
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:13
#: bookwyrm/templates/preferences/edit_user.html:68
msgid "Display preferences"
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:14
#: bookwyrm/templates/preferences/edit_user.html:100
msgid "Privacy"
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:72
msgid "Show reading goal prompt in feed:"
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:50
#: bookwyrm/templates/preferences/edit_user.html:76
msgid "Show suggested users:"
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:58
#: bookwyrm/templates/preferences/edit_user.html:85
#, python-format
msgid "Your account will show up in the <a href=\"%(path)s\">directory</a>, and may be recommended to other BookWyrm users."
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:68
msgid "Default post privacy:"
#: bookwyrm/templates/preferences/edit_user.html:89
msgid "Preferred Timezone: "
msgstr ""
#: bookwyrm/templates/preferences/edit_user.html:75
msgid "Preferred Timezone: "
#: bookwyrm/templates/preferences/edit_user.html:110
msgid "Default post privacy:"
msgstr ""
#: bookwyrm/templates/preferences/layout.html:11
msgid "Account"
msgstr ""
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr ""
#: bookwyrm/templates/preferences/layout.html:26
#: bookwyrm/templates/preferences/layout.html:27
msgid "Relationships"
msgstr ""
@ -3211,7 +3228,7 @@ msgstr ""
msgid "Show more"
msgstr ""
#: bookwyrm/templates/snippets/trimmed_text.html:34
#: bookwyrm/templates/snippets/trimmed_text.html:35
msgid "Show less"
msgstr ""
@ -3505,7 +3522,7 @@ msgstr ""
msgid "Not a valid csv file"
msgstr ""
#: bookwyrm/views/login.py:70
#: bookwyrm/views/login.py:68
msgid "Username or password are incorrect"
msgstr ""
@ -3514,8 +3531,8 @@ msgid "No user with that email address was found."
msgstr ""
#: bookwyrm/views/password.py:41
#, python-format
msgid "A password reset link sent to %s"
#, python-brace-format
msgid "A password reset link sent to {email}"
msgstr ""
#: bookwyrm/views/rss_feed.py:34

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-20 19:44+0000\n"
"POT-Creation-Date: 2021-09-28 18:31+0000\n"
"PO-Revision-Date: 2021-03-19 11:49+0800\n"
"Last-Translator: Reese Porter <reesedporter@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,59 +18,60 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: bookwyrm/forms.py:242
#: bookwyrm/forms.py:241
msgid "A user with this email already exists."
msgstr "Ya existe un usuario con ese correo electrónico."
#: bookwyrm/forms.py:256
#: bookwyrm/forms.py:255
msgid "One Day"
msgstr "Un día"
#: bookwyrm/forms.py:257
#: bookwyrm/forms.py:256
msgid "One Week"
msgstr "Una semana"
#: bookwyrm/forms.py:258
#: bookwyrm/forms.py:257
msgid "One Month"
msgstr "Un mes"
#: bookwyrm/forms.py:259
#: bookwyrm/forms.py:258
msgid "Does Not Expire"
msgstr "Nunca se vence"
#: bookwyrm/forms.py:264
#, python-format
msgid "%(count)d uses"
msgstr "%(count)d usos"
#: bookwyrm/forms.py:262
#, fuzzy, python-brace-format
#| msgid "Max uses"
msgid "{i} uses"
msgstr "Número máximo de usos"
#: bookwyrm/forms.py:267
#: bookwyrm/forms.py:263
msgid "Unlimited"
msgstr "Sin límite"
#: bookwyrm/forms.py:329
#: bookwyrm/forms.py:325
msgid "List Order"
msgstr "Orden de la lista"
#: bookwyrm/forms.py:330
#: bookwyrm/forms.py:326
msgid "Book Title"
msgstr "Título"
#: bookwyrm/forms.py:331
#: bookwyrm/forms.py:327
#: bookwyrm/templates/snippets/create_status/review.html:33
#: bookwyrm/templates/user/shelf/shelf.html:117
#: bookwyrm/templates/user/shelf/shelf.html:148
msgid "Rating"
msgstr "Calificación"
#: bookwyrm/forms.py:333 bookwyrm/templates/lists/list.html:107
#: bookwyrm/forms.py:329 bookwyrm/templates/lists/list.html:109
msgid "Sort By"
msgstr "Ordenar por"
#: bookwyrm/forms.py:337
#: bookwyrm/forms.py:333
msgid "Ascending"
msgstr "Ascendente"
#: bookwyrm/forms.py:338
#: bookwyrm/forms.py:334
msgid "Descending"
msgstr "Descendente"
@ -82,23 +83,23 @@ msgstr "Error en cargar libro"
msgid "Could not find a match for book"
msgstr "No se pudo encontrar el libro"
#: bookwyrm/models/base_model.py:13
#: bookwyrm/models/base_model.py:16
msgid "Pending"
msgstr "Pendiente"
#: bookwyrm/models/base_model.py:14
#: bookwyrm/models/base_model.py:17
msgid "Self deletion"
msgstr "Auto-eliminación"
#: bookwyrm/models/base_model.py:15
#: bookwyrm/models/base_model.py:18
msgid "Moderator suspension"
msgstr "Suspensión de moderador"
#: bookwyrm/models/base_model.py:16
#: bookwyrm/models/base_model.py:19
msgid "Moderator deletion"
msgstr "Eliminación de moderador"
#: bookwyrm/models/base_model.py:17
#: bookwyrm/models/base_model.py:20
msgid "Domain block"
msgstr "Bloqueo de dominio"
@ -125,7 +126,7 @@ msgstr "%(value)s no es un remote_id válido"
msgid "%(value)s is not a valid username"
msgstr "%(value)s no es un usuario válido"
#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:169
#: bookwyrm/models/fields.py:181 bookwyrm/templates/layout.html:171
msgid "username"
msgstr "nombre de usuario"
@ -321,7 +322,7 @@ msgstr "Clave Goodreads:"
#: bookwyrm/templates/book/readthrough.html:76
#: bookwyrm/templates/lists/bookmark_button.html:15
#: bookwyrm/templates/lists/form.html:44
#: bookwyrm/templates/preferences/edit_user.html:80
#: bookwyrm/templates/preferences/edit_user.html:118
#: bookwyrm/templates/settings/announcement_form.html:69
#: bookwyrm/templates/settings/edit_server.html:68
#: bookwyrm/templates/settings/federated_server.html:98
@ -438,7 +439,7 @@ msgstr "Sujetos"
msgid "Places"
msgstr "Lugares"
#: bookwyrm/templates/book/book.html:292 bookwyrm/templates/layout.html:73
#: bookwyrm/templates/book/book.html:292 bookwyrm/templates/layout.html:75
#: bookwyrm/templates/lists/lists.html:5 bookwyrm/templates/lists/lists.html:12
#: bookwyrm/templates/search/layout.html:25
#: bookwyrm/templates/search/layout.html:50
@ -452,7 +453,7 @@ msgstr "Agregar a lista"
#: bookwyrm/templates/book/book.html:313
#: bookwyrm/templates/book/cover_modal.html:31
#: bookwyrm/templates/lists/list.html:179
#: bookwyrm/templates/lists/list.html:181
#: bookwyrm/templates/settings/domain_form.html:26
#: bookwyrm/templates/settings/ip_address_form.html:32
msgid "Add"
@ -772,7 +773,7 @@ msgstr "Reenviar enlace de confirmación"
#: bookwyrm/templates/confirm_email/resend_form.html:11
#: bookwyrm/templates/landing/layout.html:67
#: bookwyrm/templates/password_reset_request.html:18
#: bookwyrm/templates/preferences/edit_user.html:38
#: bookwyrm/templates/preferences/edit_user.html:56
#: bookwyrm/templates/snippets/register_form.html:13
msgid "Email address:"
msgstr "Dirección de correo electrónico:"
@ -795,7 +796,7 @@ msgstr "Comunidad federalizada"
#: bookwyrm/templates/directory/directory.html:4
#: bookwyrm/templates/directory/directory.html:9
#: bookwyrm/templates/layout.html:99
#: bookwyrm/templates/layout.html:101
msgid "Directory"
msgstr "Directorio"
@ -867,7 +868,7 @@ msgstr "Todos los usuarios conocidos"
#: bookwyrm/templates/discover/discover.html:4
#: bookwyrm/templates/discover/discover.html:10
#: bookwyrm/templates/layout.html:76
#: bookwyrm/templates/layout.html:78
msgid "Discover"
msgstr "Descubrir"
@ -993,7 +994,7 @@ msgid "Direct Messages with <a href=\"%(path)s\">%(username)s</a>"
msgstr "Mensajes directos con <a href=\"%(path)s\">%(username)s</a>"
#: bookwyrm/templates/feed/direct_messages.html:10
#: bookwyrm/templates/layout.html:109
#: bookwyrm/templates/layout.html:111
msgid "Direct Messages"
msgstr "Mensajes directos"
@ -1073,7 +1074,7 @@ msgid "What are you reading?"
msgstr "¿Qué estás leyendo?"
#: bookwyrm/templates/get_started/books.html:9
#: bookwyrm/templates/layout.html:43 bookwyrm/templates/lists/list.html:135
#: bookwyrm/templates/layout.html:45 bookwyrm/templates/lists/list.html:137
msgid "Search for a book"
msgstr "Buscar libros"
@ -1091,8 +1092,8 @@ msgstr "Puedes agregar libros cuando comiences a usar %(site_name)s."
#: bookwyrm/templates/get_started/books.html:17
#: bookwyrm/templates/get_started/users.html:18
#: bookwyrm/templates/get_started/users.html:19
#: bookwyrm/templates/layout.html:49 bookwyrm/templates/layout.html:50
#: bookwyrm/templates/lists/list.html:139
#: bookwyrm/templates/layout.html:51 bookwyrm/templates/layout.html:52
#: bookwyrm/templates/lists/list.html:141
#: bookwyrm/templates/search/layout.html:4
#: bookwyrm/templates/search/layout.html:9
msgid "Search"
@ -1108,7 +1109,7 @@ msgid "Popular on %(site_name)s"
msgstr "Popular en %(site_name)s"
#: bookwyrm/templates/get_started/books.html:58
#: bookwyrm/templates/lists/list.html:152
#: bookwyrm/templates/lists/list.html:154
msgid "No books found"
msgstr "No se encontró ningún libro"
@ -1153,12 +1154,12 @@ msgid "Finish"
msgstr "Terminar"
#: bookwyrm/templates/get_started/profile.html:15
#: bookwyrm/templates/preferences/edit_user.html:24
#: bookwyrm/templates/preferences/edit_user.html:42
msgid "Display name:"
msgstr "Nombre de visualización:"
#: bookwyrm/templates/get_started/profile.html:22
#: bookwyrm/templates/preferences/edit_user.html:31
#: bookwyrm/templates/preferences/edit_user.html:49
msgid "Summary:"
msgstr "Resumen:"
@ -1167,17 +1168,17 @@ msgid "A little bit about you"
msgstr "Un poco sobre ti"
#: bookwyrm/templates/get_started/profile.html:32
#: bookwyrm/templates/preferences/edit_user.html:17
#: bookwyrm/templates/preferences/edit_user.html:27
msgid "Avatar:"
msgstr "Avatar:"
#: bookwyrm/templates/get_started/profile.html:42
#: bookwyrm/templates/preferences/edit_user.html:62
#: bookwyrm/templates/preferences/edit_user.html:104
msgid "Manually approve followers:"
msgstr "Aprobar seguidores a mano:"
#: bookwyrm/templates/get_started/profile.html:48
#: bookwyrm/templates/preferences/edit_user.html:54
#: bookwyrm/templates/preferences/edit_user.html:80
msgid "Show this account in suggested users:"
msgstr "Mostrar esta cuenta en los usuarios sugeridos:"
@ -1409,29 +1410,35 @@ msgstr "¡Gracias! Tu solicitud ha sido recibido."
msgid "Your Account"
msgstr "Tu cuenta"
#: bookwyrm/templates/layout.html:41
#: bookwyrm/templates/layout.html:13
#, fuzzy, python-format
#| msgid "About %(site_name)s"
msgid "%(site_name)s search"
msgstr "Sobre %(site_name)s"
#: bookwyrm/templates/layout.html:43
#, fuzzy
#| msgid "Search for a book or user"
msgid "Search for a book, user, or list"
msgstr "Buscar un libro o un usuario"
#: bookwyrm/templates/layout.html:59 bookwyrm/templates/layout.html:60
#: bookwyrm/templates/layout.html:61 bookwyrm/templates/layout.html:62
msgid "Main navigation menu"
msgstr "Menú de navigación central"
#: bookwyrm/templates/layout.html:70
#: bookwyrm/templates/layout.html:72
msgid "Feed"
msgstr "Actividad"
#: bookwyrm/templates/layout.html:104
#: bookwyrm/templates/layout.html:106
msgid "Your Books"
msgstr "Tus libros"
#: bookwyrm/templates/layout.html:114
#: bookwyrm/templates/layout.html:116
msgid "Settings"
msgstr "Configuración"
#: bookwyrm/templates/layout.html:123
#: bookwyrm/templates/layout.html:125
#: bookwyrm/templates/settings/layout.html:40
#: bookwyrm/templates/settings/manage_invite_requests.html:15
#: bookwyrm/templates/settings/manage_invites.html:3
@ -1439,69 +1446,69 @@ msgstr "Configuración"
msgid "Invites"
msgstr "Invitaciones"
#: bookwyrm/templates/layout.html:130
#: bookwyrm/templates/layout.html:132
msgid "Admin"
msgstr "Admin"
#: bookwyrm/templates/layout.html:137
#: bookwyrm/templates/layout.html:139
msgid "Log out"
msgstr "Cerrar sesión"
#: bookwyrm/templates/layout.html:145 bookwyrm/templates/layout.html:146
#: bookwyrm/templates/layout.html:147 bookwyrm/templates/layout.html:148
#: bookwyrm/templates/notifications.html:6
#: bookwyrm/templates/notifications.html:11
msgid "Notifications"
msgstr "Notificaciones"
#: bookwyrm/templates/layout.html:168 bookwyrm/templates/layout.html:172
#: bookwyrm/templates/layout.html:170 bookwyrm/templates/layout.html:174
#: bookwyrm/templates/login.html:21
#: bookwyrm/templates/snippets/register_form.html:4
msgid "Username:"
msgstr "Nombre de usuario:"
#: bookwyrm/templates/layout.html:173
#: bookwyrm/templates/layout.html:175
msgid "password"
msgstr "contraseña"
#: bookwyrm/templates/layout.html:174 bookwyrm/templates/login.html:40
#: bookwyrm/templates/layout.html:176 bookwyrm/templates/login.html:40
msgid "Forgot your password?"
msgstr "¿Olvidaste tu contraseña?"
#: bookwyrm/templates/layout.html:177 bookwyrm/templates/login.html:7
#: bookwyrm/templates/layout.html:179 bookwyrm/templates/login.html:7
#: bookwyrm/templates/login.html:37
msgid "Log in"
msgstr "Iniciar sesión"
#: bookwyrm/templates/layout.html:185
#: bookwyrm/templates/layout.html:187
msgid "Join"
msgstr "Unirse"
#: bookwyrm/templates/layout.html:219
#: bookwyrm/templates/layout.html:221
msgid "Successfully posted status"
msgstr "Status publicado exitosamente"
#: bookwyrm/templates/layout.html:220
#: bookwyrm/templates/layout.html:222
msgid "Error posting status"
msgstr "Error en publicar status"
#: bookwyrm/templates/layout.html:228
#: bookwyrm/templates/layout.html:230
msgid "About this instance"
msgstr "Sobre esta instancia"
#: bookwyrm/templates/layout.html:232
#: bookwyrm/templates/layout.html:234
msgid "Contact site admin"
msgstr "Contactarse con administradores del sitio"
#: bookwyrm/templates/layout.html:236
#: bookwyrm/templates/layout.html:238
msgid "Documentation"
msgstr "Documentación de Django"
#: bookwyrm/templates/layout.html:243
#: bookwyrm/templates/layout.html:245
#, python-format
msgid "Support %(site_name)s on <a href=\"%(support_link)s\" target=\"_blank\">%(support_title)s</a>"
msgstr "Apoyar %(site_name)s en <a href=\"%(support_link)s\" target=\"_blank\">%(support_title)s</a>"
#: bookwyrm/templates/layout.html:247
#: bookwyrm/templates/layout.html:249
msgid "BookWyrm's source code is freely available. You can contribute or report issues on <a href=\"https://github.com/mouse-reeve/bookwyrm\">GitHub</a>."
msgstr "BookWyrm es software de código abierto. Puedes contribuir o reportar problemas en <a href=\"https://github.com/mouse-reeve/bookwyrm\">GitHub</a>."
@ -1601,7 +1608,7 @@ msgstr "Abierto"
msgid "Anyone can add books to this list"
msgstr "Cualquer usuario puede agregar libros a esta lista"
#: bookwyrm/templates/lists/form.html:49
#: bookwyrm/templates/lists/form.html:50
msgid "Delete list"
msgstr "Eliminar lista"
@ -1622,7 +1629,7 @@ msgstr "Esta lista está vacia"
msgid "Added by <a href=\"%(user_path)s\">%(username)s</a>"
msgstr "Agregado por <a href=\"%(user_path)s\">%(username)s</a>"
#: bookwyrm/templates/lists/list.html:74
#: bookwyrm/templates/lists/list.html:75
msgid "List position"
msgstr "Posición"
@ -1630,42 +1637,42 @@ msgstr "Posición"
msgid "Set"
msgstr "Establecido"
#: bookwyrm/templates/lists/list.html:89
#: bookwyrm/templates/lists/list.html:91
#: bookwyrm/templates/snippets/shelf_selector.html:26
msgid "Remove"
msgstr "Quitar"
#: bookwyrm/templates/lists/list.html:103
#: bookwyrm/templates/lists/list.html:120
#: bookwyrm/templates/lists/list.html:105
#: bookwyrm/templates/lists/list.html:122
msgid "Sort List"
msgstr "Ordena la lista"
#: bookwyrm/templates/lists/list.html:113
#: bookwyrm/templates/lists/list.html:115
msgid "Direction"
msgstr "Dirección"
#: bookwyrm/templates/lists/list.html:127
#: bookwyrm/templates/lists/list.html:129
msgid "Add Books"
msgstr "Agregar libros"
#: bookwyrm/templates/lists/list.html:129
#: bookwyrm/templates/lists/list.html:131
msgid "Suggest Books"
msgstr "Sugerir libros"
#: bookwyrm/templates/lists/list.html:140
#: bookwyrm/templates/lists/list.html:142
msgid "search"
msgstr "buscar"
#: bookwyrm/templates/lists/list.html:146
#: bookwyrm/templates/lists/list.html:148
msgid "Clear search"
msgstr "Borrar búsqueda"
#: bookwyrm/templates/lists/list.html:151
#: bookwyrm/templates/lists/list.html:153
#, python-format
msgid "No books found matching the query \"%(query)s\""
msgstr "No se encontró ningún libro correspondiente a la búsqueda: \"%(query)s\""
#: bookwyrm/templates/lists/list.html:179
#: bookwyrm/templates/lists/list.html:181
msgid "Suggest"
msgstr "Sugerir"
@ -1929,7 +1936,7 @@ msgstr "Restablecer contraseña"
#: bookwyrm/templates/preferences/blocks.html:4
#: bookwyrm/templates/preferences/blocks.html:7
#: bookwyrm/templates/preferences/layout.html:30
#: bookwyrm/templates/preferences/layout.html:31
msgid "Blocked Users"
msgstr "Usuarios bloqueados"
@ -1940,7 +1947,7 @@ msgstr "No hay ningún usuario bloqueado actualmente."
#: bookwyrm/templates/preferences/change_password.html:4
#: bookwyrm/templates/preferences/change_password.html:7
#: bookwyrm/templates/preferences/change_password.html:21
#: bookwyrm/templates/preferences/layout.html:19
#: bookwyrm/templates/preferences/layout.html:20
msgid "Change Password"
msgstr "Cambiar contraseña"
@ -1951,7 +1958,7 @@ msgstr "Nueva contraseña:"
#: bookwyrm/templates/preferences/delete_user.html:4
#: bookwyrm/templates/preferences/delete_user.html:7
#: bookwyrm/templates/preferences/delete_user.html:26
#: bookwyrm/templates/preferences/layout.html:23
#: bookwyrm/templates/preferences/layout.html:24
#: bookwyrm/templates/user_admin/delete_user_form.html:23
msgid "Delete Account"
msgstr "Quitar cuenta"
@ -1966,40 +1973,56 @@ msgstr "Eliminar tu cuenta no puede ser deshecho. El nombre de usuario no será
#: bookwyrm/templates/preferences/edit_user.html:4
#: bookwyrm/templates/preferences/edit_user.html:7
#: bookwyrm/templates/preferences/layout.html:15
msgid "Edit Profile"
msgstr "Editar perfil"
#: bookwyrm/templates/preferences/edit_user.html:46
#: bookwyrm/templates/preferences/edit_user.html:12
#: bookwyrm/templates/preferences/edit_user.html:25
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Perfil"
#: bookwyrm/templates/preferences/edit_user.html:13
#: bookwyrm/templates/preferences/edit_user.html:68
#, fuzzy
#| msgid "Email preference"
msgid "Display preferences"
msgstr "Preferencia de correo electrónico"
#: bookwyrm/templates/preferences/edit_user.html:14
#: bookwyrm/templates/preferences/edit_user.html:100
#, fuzzy
#| msgid "Post privacy"
msgid "Privacy"
msgstr "Privacidad de publicación"
#: bookwyrm/templates/preferences/edit_user.html:72
msgid "Show reading goal prompt in feed:"
msgstr "Mostrar sugerencia de meta de lectura en el feed:"
#: bookwyrm/templates/preferences/edit_user.html:50
#: bookwyrm/templates/preferences/edit_user.html:76
msgid "Show suggested users:"
msgstr "Mostrar usuarios sugeridos:"
#: bookwyrm/templates/preferences/edit_user.html:58
#: bookwyrm/templates/preferences/edit_user.html:85
#, python-format
msgid "Your account will show up in the <a href=\"%(path)s\">directory</a>, and may be recommended to other BookWyrm users."
msgstr "Tu cuenta se aparecerá en el <a href=\"%(path)s\">directorio</a>, y puede ser recomendado a otros usuarios de BookWyrm."
#: bookwyrm/templates/preferences/edit_user.html:68
msgid "Default post privacy:"
msgstr "Privacidad de publicación por defecto:"
#: bookwyrm/templates/preferences/edit_user.html:75
#: bookwyrm/templates/preferences/edit_user.html:89
msgid "Preferred Timezone: "
msgstr "Huso horario preferido"
#: bookwyrm/templates/preferences/edit_user.html:110
msgid "Default post privacy:"
msgstr "Privacidad de publicación por defecto:"
#: bookwyrm/templates/preferences/layout.html:11
msgid "Account"
msgstr "Cuenta"
#: bookwyrm/templates/preferences/layout.html:15
#: bookwyrm/templates/user_admin/user_info.html:7
msgid "Profile"
msgstr "Perfil"
#: bookwyrm/templates/preferences/layout.html:26
#: bookwyrm/templates/preferences/layout.html:27
msgid "Relationships"
msgstr "Relaciones"
@ -3232,7 +3255,7 @@ msgstr "En orden descendente"
msgid "Show more"
msgstr "Mostrar más"
#: bookwyrm/templates/snippets/trimmed_text.html:34
#: bookwyrm/templates/snippets/trimmed_text.html:35
msgid "Show less"
msgstr "Mostrar menos"
@ -3526,7 +3549,7 @@ msgstr "%(title)s: %(subtitle)s"
msgid "Not a valid csv file"
msgstr "No un archivo csv válido"
#: bookwyrm/views/login.py:70
#: bookwyrm/views/login.py:68
msgid "Username or password are incorrect"
msgstr "Nombre de usuario o contraseña es incorrecta"
@ -3535,8 +3558,9 @@ msgid "No user with that email address was found."
msgstr "No se pudo encontrar un usuario con esa dirección de correo electrónico."
#: bookwyrm/views/password.py:41
#, python-format
msgid "A password reset link sent to %s"
#, fuzzy, python-brace-format
#| msgid "A password reset link sent to %s"
msgid "A password reset link sent to {email}"
msgstr "Un enlace para reestablecer tu contraseña se enviará a %s"
#: bookwyrm/views/rss_feed.py:34
@ -3544,6 +3568,9 @@ msgstr "Un enlace para reestablecer tu contraseña se enviará a %s"
msgid "Status updates from {obj.display_name}"
msgstr "Actualizaciones de status de {obj.display_name}"
#~ msgid "%(count)d uses"
#~ msgstr "%(count)d usos"
#~ msgid "This instance is closed"
#~ msgstr "Esta instancia está cerrada."

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