forked from mirrors/bookwyrm
Merge branch 'main' into production
This commit is contained in:
commit
588d2da2c6
29 changed files with 258 additions and 158 deletions
|
@ -4,6 +4,7 @@ from django import forms
|
|||
from bookwyrm import models
|
||||
from bookwyrm.models.fields import ClearableFileInputWithWarning
|
||||
from .custom_form import CustomForm
|
||||
from .widgets import ArrayWidget, SelectDateWidget, Select
|
||||
|
||||
|
||||
# pylint: disable=missing-class-docstring
|
||||
|
@ -14,14 +15,6 @@ class CoverForm(CustomForm):
|
|||
help_texts = {f: None for f in fields}
|
||||
|
||||
|
||||
class ArrayWidget(forms.widgets.TextInput):
|
||||
# pylint: disable=unused-argument
|
||||
# pylint: disable=no-self-use
|
||||
def value_from_datadict(self, data, files, name):
|
||||
"""get all values for this name"""
|
||||
return [i for i in data.getlist(name) if i]
|
||||
|
||||
|
||||
class EditionForm(CustomForm):
|
||||
class Meta:
|
||||
model = models.Edition
|
||||
|
@ -56,16 +49,16 @@ class EditionForm(CustomForm):
|
|||
"publishers": forms.TextInput(
|
||||
attrs={"aria-describedby": "desc_publishers_help desc_publishers"}
|
||||
),
|
||||
"first_published_date": forms.SelectDateWidget(
|
||||
"first_published_date": SelectDateWidget(
|
||||
attrs={"aria-describedby": "desc_first_published_date"}
|
||||
),
|
||||
"published_date": forms.SelectDateWidget(
|
||||
"published_date": SelectDateWidget(
|
||||
attrs={"aria-describedby": "desc_published_date"}
|
||||
),
|
||||
"cover": ClearableFileInputWithWarning(
|
||||
attrs={"aria-describedby": "desc_cover"}
|
||||
),
|
||||
"physical_format": forms.Select(
|
||||
"physical_format": Select(
|
||||
attrs={"aria-describedby": "desc_physical_format"}
|
||||
),
|
||||
"physical_format_detail": forms.TextInput(
|
||||
|
|
70
bookwyrm/forms/widgets.py
Normal file
70
bookwyrm/forms/widgets.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
""" using django model forms """
|
||||
from django import forms
|
||||
|
||||
|
||||
class ArrayWidget(forms.widgets.TextInput):
|
||||
"""Inputs for postgres array fields"""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
# pylint: disable=no-self-use
|
||||
def value_from_datadict(self, data, files, name):
|
||||
"""get all values for this name"""
|
||||
return [i for i in data.getlist(name) if i]
|
||||
|
||||
|
||||
class Select(forms.Select):
|
||||
"""custom template for select widget"""
|
||||
|
||||
template_name = "widgets/select.html"
|
||||
|
||||
|
||||
class SelectDateWidget(forms.SelectDateWidget):
|
||||
"""
|
||||
A widget that splits date input into two <select> boxes and a numerical year.
|
||||
"""
|
||||
|
||||
template_name = "widgets/addon_multiwidget.html"
|
||||
select_widget = Select
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
"""sets individual widgets"""
|
||||
context = super().get_context(name, value, attrs)
|
||||
date_context = {}
|
||||
year_name = self.year_field % name
|
||||
date_context["year"] = forms.NumberInput().get_context(
|
||||
name=year_name,
|
||||
value=context["widget"]["value"]["year"],
|
||||
attrs={
|
||||
**context["widget"]["attrs"],
|
||||
"id": f"id_{year_name}",
|
||||
"class": "input",
|
||||
},
|
||||
)
|
||||
month_choices = list(self.months.items())
|
||||
if not self.is_required:
|
||||
month_choices.insert(0, self.month_none_value)
|
||||
month_name = self.month_field % name
|
||||
date_context["month"] = self.select_widget(
|
||||
attrs, choices=month_choices
|
||||
).get_context(
|
||||
name=month_name,
|
||||
value=context["widget"]["value"]["month"],
|
||||
attrs={**context["widget"]["attrs"], "id": f"id_{month_name}"},
|
||||
)
|
||||
day_choices = [(i, i) for i in range(1, 32)]
|
||||
if not self.is_required:
|
||||
day_choices.insert(0, self.day_none_value)
|
||||
day_name = self.day_field % name
|
||||
date_context["day"] = self.select_widget(
|
||||
attrs,
|
||||
choices=day_choices,
|
||||
).get_context(
|
||||
name=day_name,
|
||||
value=context["widget"]["value"]["day"],
|
||||
attrs={**context["widget"]["attrs"], "id": f"id_{day_name}"},
|
||||
)
|
||||
subwidgets = []
|
||||
for field in self._parse_date_fmt():
|
||||
subwidgets.append(date_context[field]["widget"])
|
||||
context["widget"]["subwidgets"] = subwidgets
|
||||
return context
|
|
@ -129,14 +129,6 @@ button:focus-visible .button-invisible-overlay {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/** Tooltips
|
||||
******************************************************************************/
|
||||
|
||||
.tooltip {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/** States
|
||||
******************************************************************************/
|
||||
|
||||
|
|
|
@ -155,8 +155,7 @@
|
|||
<label class="label" for="id_first_published_date">
|
||||
{% trans "First published date:" %}
|
||||
</label>
|
||||
<input type="date" name="first_published_date" class="input" id="id_first_published_date"{% if form.first_published_date.value %} value="{{ form.first_published_date.value|date:'Y-m-d' }}"{% endif %} aria-describedby="desc_first_published_date">
|
||||
|
||||
{{ form.first_published_date }}
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.first_published_date.errors id="desc_first_published_date" %}
|
||||
</div>
|
||||
|
||||
|
@ -164,7 +163,7 @@
|
|||
<label class="label" for="id_published_date">
|
||||
{% trans "Published date:" %}
|
||||
</label>
|
||||
<input type="date" name="published_date" class="input" id="id_published_date"{% if form.published_date.value %} value="{{ form.published_date.value|date:'Y-m-d'}}"{% endif %} aria-describedby="desc_published_date">
|
||||
{{ form.published_date }}
|
||||
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.published_date.errors id="desc_published_date" %}
|
||||
</div>
|
||||
|
@ -259,9 +258,7 @@
|
|||
<label class="label" for="id_physical_format">
|
||||
{% trans "Format:" %}
|
||||
</label>
|
||||
<div class="select">
|
||||
{{ form.physical_format }}
|
||||
</div>
|
||||
{{ form.physical_format }}
|
||||
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.physical_format.errors id="desc_physical_format" %}
|
||||
</div>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% trans "Help" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text class="ml-3 is-rounded is-small has-background-body p-0 pb-1" icon="question-circle is-size-6" controls_text=controls_text controls_uid=controls_uid %}
|
||||
|
||||
<aside class="tooltip notification is-hidden transition-y is-pulled-left mb-2" id="{{ controls_text }}{% if controls_uid %}-{{ controls_uid }}{% endif %}">
|
||||
{% trans "Close" as button_text %}
|
||||
{% include 'snippets/toggle/close_button.html' with label=button_text class="delete" nonbutton=True controls_text=controls_text controls_uid=controls_uid %}
|
||||
|
||||
{% block tooltip_content %}{% endblock %}
|
||||
</aside>
|
|
@ -29,9 +29,16 @@
|
|||
</section>
|
||||
|
||||
<section class="block">
|
||||
{% trans "Can't find your code?" as button_text %}
|
||||
{% include "snippets/toggle/open_button.html" with text=button_text controls_text="resend_form" focus="resend_form_header" %}
|
||||
{% include "confirm_email/resend_form.html" with controls_text="resend_form" %}
|
||||
<form name="fallback" method="GET" action="{% url 'resend-link' %}" autocomplete="off">
|
||||
<button
|
||||
type="submit"
|
||||
class="button"
|
||||
data-modal-open="resend_form"
|
||||
>
|
||||
{% trans "Can't find your code?" %}
|
||||
</button>
|
||||
</form>
|
||||
{% include "confirm_email/resend_modal.html" with id="resend_form" %}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
|
10
bookwyrm/templates/confirm_email/resend.html
Normal file
10
bookwyrm/templates/confirm_email/resend.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
{% extends 'landing/layout.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}
|
||||
{% trans "Resend confirmation link" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "confirm_email/resend_modal.html" with active=True static=True id="resend-modal" %}
|
||||
{% endblock %}
|
|
@ -1,20 +0,0 @@
|
|||
{% extends "components/inline_form.html" %}
|
||||
{% load i18n %}
|
||||
{% block header %}
|
||||
{% trans "Resend confirmation link" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<form name="resend" method="post" action="{% url 'resend-link' %}">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label class="label" for="email">{% trans "Email address:" %}</label>
|
||||
<div class="control">
|
||||
<input type="text" name="email" class="input" required id="email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-link">{% trans "Resend link" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
44
bookwyrm/templates/confirm_email/resend_modal.html
Normal file
44
bookwyrm/templates/confirm_email/resend_modal.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
{% extends "components/modal.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-title %}
|
||||
{% trans "Resend confirmation link" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-form-open %}
|
||||
<form name="resend" method="post" action="{% url 'resend-link' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label class="label" for="email">{% trans "Email address:" %}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
class="input"
|
||||
id="email"
|
||||
aria-described-by="id_email_errors"
|
||||
required
|
||||
>
|
||||
{% if error %}
|
||||
<div id="id_email_errors">
|
||||
<p class="help is-danger">
|
||||
{% trans "No user matching this email address found." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<div class="control">
|
||||
<button class="button is-link">{% trans "Resend link" %}</button>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-form-close %}
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -5,7 +5,19 @@
|
|||
<section class="block">
|
||||
<h2 class="title is-4">{% trans "Your Books" %}</h2>
|
||||
{% if not suggested_books %}
|
||||
<p>{% trans "There are no books here right now! Try searching for a book to get started" %}</p>
|
||||
|
||||
<div class="content">
|
||||
<p>{% trans "There are no books here right now! Try searching for a book to get started" %}</p>
|
||||
|
||||
<div class="box has-background-link-light">
|
||||
<p>{% trans "Do you have book data from another service like GoodReads?" %}</p>
|
||||
<a href="{% url 'import' %}">
|
||||
<span class="icon icon-list" aria-hidden="true"></span>
|
||||
{% trans "Import your reading history" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
{% with active_book=request.GET.book %}
|
||||
<div class="tab-group">
|
||||
|
|
|
@ -14,28 +14,32 @@
|
|||
<div class="column is-half">
|
||||
|
||||
<div class="field">
|
||||
<label class="label is-pulled-left" for="source">
|
||||
<label class="label" for="source">
|
||||
{% trans "Data source:" %}
|
||||
</label>
|
||||
{% include 'import/tooltip.html' with controls_text="goodreads-tooltip" %}
|
||||
|
||||
<div class="select">
|
||||
<select name="source" id="source" aria-describedby="desc_source">
|
||||
<option value="Goodreads" {% if current == 'Goodreads' %}selected{% endif %}>
|
||||
Goodreads (CSV)
|
||||
</option>
|
||||
<option value="Storygraph" {% if current == 'Storygraph' %}selected{% endif %}>
|
||||
Storygraph (CSV)
|
||||
</option>
|
||||
<option value="LibraryThing" {% if current == 'LibraryThing' %}selected{% endif %}>
|
||||
LibraryThing (TSV)
|
||||
</option>
|
||||
<option value="OpenLibrary" {% if current == 'OpenLibrary' %}selected{% endif %}>
|
||||
OpenLibrary (CSV)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<p class="help" id="desc_source">
|
||||
{% trans 'You can download your Goodreads data from the <a href="https://www.goodreads.com/review/import" target="_blank" rel="noopener noreferrer">Import/Export page</a> of your Goodreads account.' %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="select block">
|
||||
<select name="source" id="source">
|
||||
<option value="Goodreads" {% if current == 'Goodreads' %}selected{% endif %}>
|
||||
Goodreads (CSV)
|
||||
</option>
|
||||
<option value="Storygraph" {% if current == 'Storygraph' %}selected{% endif %}>
|
||||
Storygraph (CSV)
|
||||
</option>
|
||||
<option value="LibraryThing" {% if current == 'LibraryThing' %}selected{% endif %}>
|
||||
LibraryThing (TSV)
|
||||
</option>
|
||||
<option value="OpenLibrary" {% if current == 'OpenLibrary' %}selected{% endif %}>
|
||||
OpenLibrary (CSV)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_csv_file">{% trans "Data file:" %}</label>
|
||||
{{ import_form.csv_file }}
|
||||
|
@ -63,7 +67,7 @@
|
|||
<div class="content block">
|
||||
<h2 class="title">{% trans "Recent Imports" %}</h2>
|
||||
{% if not jobs %}
|
||||
<p>{% trans "No recent imports" %}</p>
|
||||
<p><em>{% trans "No recent imports" %}</em></p>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for job in jobs %}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{% extends 'components/tooltip.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block tooltip_content %}
|
||||
|
||||
{% trans 'You can download your Goodreads data from the <a href="https://www.goodreads.com/review/import" target="_blank" rel="noopener noreferrer">Import/Export page</a> of your Goodreads account.' %}
|
||||
|
||||
{% endblock %}
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
{% block panel %}
|
||||
<div class="block table-container">
|
||||
<table class="table is-striped">
|
||||
<table class="table is-striped is-fullwidth">
|
||||
<tr>
|
||||
<th>
|
||||
{% url 'settings-announcements' as url %}
|
||||
|
|
|
@ -154,7 +154,7 @@
|
|||
</summary>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table is-striped">
|
||||
<table class="table is-striped is-fullwidth">
|
||||
<tr>
|
||||
<th>
|
||||
<label for="id_string_match">{% trans "String match" %}</label>
|
||||
|
|
|
@ -10,26 +10,26 @@
|
|||
{% block panel %}
|
||||
|
||||
<div class="columns block has-text-centered is-mobile is-multiline">
|
||||
<div class="column is-3-desktop is-6-mobile">
|
||||
<div class="notification">
|
||||
<div class="column is-3-desktop is-6-mobile is-flex">
|
||||
<div class="notification is-flex-grow-1">
|
||||
<h3>{% trans "Total users" %}</h3>
|
||||
<p class="title is-5">{{ users|intcomma }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-3-desktop is-6-mobile">
|
||||
<div class="notification">
|
||||
<div class="column is-3-desktop is-6-mobil is-flexe">
|
||||
<div class="notification is-flex-grow-1">
|
||||
<h3>{% trans "Active this month" %}</h3>
|
||||
<p class="title is-5">{{ active_users|intcomma }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-3-desktop is-6-mobile">
|
||||
<div class="notification">
|
||||
<div class="column is-3-desktop is-6-mobile is-flex">
|
||||
<div class="notification is-flex-grow-1">
|
||||
<h3>{% trans "Statuses" %}</h3>
|
||||
<p class="title is-5">{{ statuses|intcomma }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-3-desktop is-6-mobile">
|
||||
<div class="notification">
|
||||
<div class="column is-3-desktop is-6-mobile is-flex">
|
||||
<div class="notification is-flex-grow-1">
|
||||
<h3>{% trans "Works" %}</h3>
|
||||
<p class="title is-5">{{ works|intcomma }}</p>
|
||||
</div>
|
||||
|
@ -38,8 +38,8 @@
|
|||
|
||||
<div class="columns block is-multiline">
|
||||
{% if reports %}
|
||||
<div class="column">
|
||||
<a href="{% url 'settings-reports' %}" class="notification is-warning is-block">
|
||||
<div class="column is-flex">
|
||||
<a href="{% url 'settings-reports' %}" class="notification is-warning is-block is-flex-grow-1">
|
||||
{% blocktrans trimmed count counter=reports with display_count=reports|intcomma %}
|
||||
{{ display_count }} open report
|
||||
{% plural %}
|
||||
|
@ -50,8 +50,8 @@
|
|||
{% endif %}
|
||||
|
||||
{% if pending_domains %}
|
||||
<div class="column">
|
||||
<a href="{% url 'settings-link-domain' %}" class="notification is-primary is-block">
|
||||
<div class="column is-flex">
|
||||
<a href="{% url 'settings-link-domain' %}" class="notification is-primary is-block is-flex-grow-1">
|
||||
{% blocktrans trimmed count counter=pending_domains with display_count=pending_domains|intcomma %}
|
||||
{{ display_count }} domain needs review
|
||||
{% plural %}
|
||||
|
@ -62,8 +62,8 @@
|
|||
{% endif %}
|
||||
|
||||
{% if not site.allow_registration and site.allow_invite_requests and invite_requests %}
|
||||
<div class="column">
|
||||
<a href="{% url 'settings-invite-requests' %}" class="notification is-block is-success">
|
||||
<div class="column is-flex">
|
||||
<a href="{% url 'settings-invite-requests' %}" class="notification is-block is-success is-flex-grow-1">
|
||||
{% blocktrans trimmed count counter=invite_requests with display_count=invite_requests|intcomma %}
|
||||
{{ display_count }} invite request
|
||||
{% plural %}
|
||||
|
@ -74,8 +74,8 @@
|
|||
{% endif %}
|
||||
|
||||
{% if current_version %}
|
||||
<div class="column">
|
||||
<a href="https://docs.joinbookwyrm.com/updating-your-instance.html" class="notification is-block is-warning" target="_blank">
|
||||
<div class="column is-flex">
|
||||
<a href="https://docs.joinbookwyrm.com/updating-your-instance.html" class="notification is-block is-warning is-flex-grow-1" target="_blank">
|
||||
{% blocktrans trimmed with current=current_version available=available_version %}
|
||||
An update is available! You're running v{{ current }} and the latest release is {{ available }}.
|
||||
{% endblocktrans %}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<table class="table is-striped">
|
||||
<table class="table is-striped is-fullwidth">
|
||||
<tr>
|
||||
{% url 'settings-federation' as url %}
|
||||
<th>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
<div class="field">
|
||||
<input type="text" name="address" maxlength="255" class="input" required="" id="id_address" placeholder="190.0.2.0/24" aria-describedby="desc_address">
|
||||
<p class="help">{% trans "You can block IP ranges using CIDR syntax." %}</p>
|
||||
</div>
|
||||
|
||||
{% include 'snippets/form_errors.html' with errors_list=form.address.errors id="desc_address" %}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{% extends 'components/tooltip.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block tooltip_content %}
|
||||
|
||||
{% trans "You can block IP ranges using CIDR syntax." %}
|
||||
|
||||
{% endblock %}
|
|
@ -93,7 +93,7 @@
|
|||
</ul>
|
||||
{% endif %}
|
||||
</nav>
|
||||
<div class="column">
|
||||
<div class="column is-clipped">
|
||||
{% block panel %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -139,6 +139,13 @@
|
|||
{% trans "Allow registration" %}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label mb-0" for="id_require_confirm_email">
|
||||
{{ site_form.require_confirm_email }}
|
||||
{% trans "Require users to confirm email address" %}
|
||||
</label>
|
||||
<p class="help" id="desc_require_confirm_email">{% trans "(Recommended if registration is open)" %}</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_allow_invite_requests">
|
||||
{{ site_form.allow_invite_requests }}
|
||||
|
@ -157,13 +164,6 @@
|
|||
{{ site_form.invite_question_text }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label mb-0" for="id_require_confirm_email">
|
||||
{{ site_form.require_confirm_email }}
|
||||
{% trans "Require users to confirm email address" %}
|
||||
</label>
|
||||
<p class="help" id="desc_require_confirm_email">{% trans "(Recommended if registration is open)" %}</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label" for="id_registration_closed_text">{% trans "Registration closed text:" %}</label>
|
||||
{{ site_form.registration_closed_text }}
|
||||
|
@ -171,7 +171,7 @@
|
|||
<div class="field">
|
||||
<label class="label" for="id_invite_request_text">{% trans "Invite request text:" %}</label>
|
||||
{{ site_form.invite_request_text }}
|
||||
|
||||
|
||||
{% include 'snippets/form_errors.html' with errors_list=site_form.invite_request_text.errors id="desc_invite_request_text" %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
<section class="block content">
|
||||
<h2 class="title is-4">{% trans "Available Themes" %}</h2>
|
||||
<div class="table-container">
|
||||
<table class="table is-striped">
|
||||
<table class="table is-striped is-fullwidth">
|
||||
<tr>
|
||||
<th>
|
||||
{% trans "Theme name" %}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</div>
|
||||
|
||||
<div class="table-container block">
|
||||
<table class="table is-striped">
|
||||
<table class="table is-striped is-fullwidth">
|
||||
<tr>
|
||||
{% url 'settings-users' as url %}
|
||||
<th>
|
||||
|
|
9
bookwyrm/templates/widgets/addon_multiwidget.html
Normal file
9
bookwyrm/templates/widgets/addon_multiwidget.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% spaceless %}
|
||||
<div class="field has-addons">
|
||||
{% for widget in widget.subwidgets %}
|
||||
<div class="control{% if forloop.last %} is-expanded{% endif %}">
|
||||
{% include widget.template_name %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endspaceless %}
|
10
bookwyrm/templates/widgets/select.html
Normal file
10
bookwyrm/templates/widgets/select.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<div class="select">
|
||||
<select
|
||||
name="{{ widget.name }}"
|
||||
{% include "django/forms/widgets/attrs.html" %}
|
||||
>{% for group_name, group_choices, group_index in widget.optgroups %}{% if group_name %}
|
||||
<optgroup label="{{ group_name }}">{% endif %}{% for option in group_choices %}
|
||||
{% include option.template_name with widget=option %}{% endfor %}{% if group_name %}
|
||||
</optgroup>{% endif %}{% endfor %}
|
||||
</select>
|
||||
</div>
|
|
@ -360,10 +360,17 @@ class RegisterViews(TestCase):
|
|||
result = view(request)
|
||||
validate_html(result.render())
|
||||
|
||||
def test_resend_link(self, *_):
|
||||
def test_resend_link_get(self, *_):
|
||||
"""try again"""
|
||||
request = self.factory.get("")
|
||||
request.user = self.anonymous_user
|
||||
result = views.ResendConfirmEmail.as_view()(request)
|
||||
validate_html(result.render())
|
||||
|
||||
def test_resend_link_post(self, *_):
|
||||
"""try again"""
|
||||
request = self.factory.post("", {"email": "mouse@mouse.com"})
|
||||
request.user = self.anonymous_user
|
||||
with patch("bookwyrm.emailing.send_email.delay") as mock:
|
||||
views.resend_link(request)
|
||||
views.ResendConfirmEmail.as_view()(request)
|
||||
self.assertEqual(mock.call_count, 1)
|
||||
|
|
|
@ -71,7 +71,7 @@ urlpatterns = [
|
|||
views.ConfirmEmailCode.as_view(),
|
||||
name="confirm-email-code",
|
||||
),
|
||||
re_path(r"^resend-link/?$", views.resend_link, name="resend-link"),
|
||||
re_path(r"^resend-link/?$", views.ResendConfirmEmail.as_view(), name="resend-link"),
|
||||
re_path(r"^logout/?$", views.Logout.as_view(), name="logout"),
|
||||
re_path(
|
||||
r"^password-reset/?$",
|
||||
|
|
|
@ -52,7 +52,8 @@ from .books.links import BookFileLinks, AddFileLink, delete_link
|
|||
from .landing.about import about, privacy, conduct
|
||||
from .landing.landing import Home, Landing
|
||||
from .landing.login import Login, Logout
|
||||
from .landing.register import Register, ConfirmEmail, ConfirmEmailCode, resend_link
|
||||
from .landing.register import Register
|
||||
from .landing.register import ConfirmEmail, ConfirmEmailCode, ResendConfirmEmail
|
||||
from .landing.password import PasswordResetRequest, PasswordReset
|
||||
|
||||
# shelves
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
""" the good stuff! the books! """
|
||||
from re import sub, findall
|
||||
from dateutil.parser import parse as dateparse
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.postgres.search import SearchRank, SearchVector
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.datastructures import MultiValueDictKeyError
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views import View
|
||||
|
@ -52,7 +50,6 @@ class EditBook(View):
|
|||
|
||||
# either of the above cases requires additional confirmation
|
||||
if data.get("add_author"):
|
||||
data = copy_form(data)
|
||||
return TemplateResponse(request, "book/edit/edit_book.html", data)
|
||||
|
||||
remove_authors = request.POST.getlist("remove_authors")
|
||||
|
@ -118,7 +115,6 @@ class CreateBook(View):
|
|||
|
||||
# go to confirm mode
|
||||
if not parent_work_id or data.get("add_author"):
|
||||
data = copy_form(data)
|
||||
return TemplateResponse(request, "book/edit/edit_book.html", data)
|
||||
|
||||
with transaction.atomic():
|
||||
|
@ -139,21 +135,6 @@ class CreateBook(View):
|
|||
return redirect(f"/book/{book.id}")
|
||||
|
||||
|
||||
def copy_form(data):
|
||||
"""helper to re-create the date fields in the form"""
|
||||
formcopy = data["form"].data.copy()
|
||||
try:
|
||||
formcopy["first_published_date"] = dateparse(formcopy["first_published_date"])
|
||||
except (MultiValueDictKeyError, ValueError):
|
||||
pass
|
||||
try:
|
||||
formcopy["published_date"] = dateparse(formcopy["published_date"])
|
||||
except (MultiValueDictKeyError, ValueError):
|
||||
pass
|
||||
data["form"].data = formcopy
|
||||
return data
|
||||
|
||||
|
||||
def add_authors(request, data):
|
||||
"""helper for adding authors"""
|
||||
add_author = [author for author in request.POST.getlist("add_author") if author]
|
||||
|
|
|
@ -5,7 +5,6 @@ from django.shortcuts import get_object_or_404, redirect
|
|||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters
|
||||
|
||||
from bookwyrm import emailing, forms, models
|
||||
|
@ -129,12 +128,22 @@ class ConfirmEmail(View):
|
|||
return ConfirmEmailCode().get(request, code)
|
||||
|
||||
|
||||
@require_POST
|
||||
def resend_link(request):
|
||||
"""resend confirmation link"""
|
||||
email = request.POST.get("email")
|
||||
user = get_object_or_404(models.User, email=email)
|
||||
emailing.email_confirmation_email(user)
|
||||
return TemplateResponse(
|
||||
request, "confirm_email/confirm_email.html", {"valid": True}
|
||||
)
|
||||
class ResendConfirmEmail(View):
|
||||
"""you probably didn't get the email because celery is slow but you can try this"""
|
||||
|
||||
def get(self, request, error=False):
|
||||
"""resend link landing page"""
|
||||
return TemplateResponse(request, "confirm_email/resend.html", {"error": error})
|
||||
|
||||
def post(self, request):
|
||||
"""resend confirmation link"""
|
||||
email = request.POST.get("email")
|
||||
try:
|
||||
user = models.User.objects.get(email=email)
|
||||
except models.User.DoesNotExist:
|
||||
return self.get(request, error=True)
|
||||
|
||||
emailing.email_confirmation_email(user)
|
||||
return TemplateResponse(
|
||||
request, "confirm_email/confirm_email.html", {"valid": True}
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue