feat: added recover password view
This commit is contained in:
@@ -43,6 +43,7 @@ EMAIL_HOST = env.str('EMAIL_HOST', '')
|
||||
EMAIL_HOST_USER = env.str('EMAIL_HOST_USER', '')
|
||||
EMAIL_HOST_PASSWORD = env.str('EMAIL_HOST_PASSWORD', '')
|
||||
EMAIL_PORT = env.int('EMAIL_PORT', 587)
|
||||
DEFAULT_FROM_EMAIL = env.str('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
|
||||
|
||||
# Nivel de log de la app y de Django (ver LOGGING en base.py). En producción
|
||||
# (DEBUG=False) Django, por defecto, solo manda los errores por email a
|
||||
|
||||
+29
-1
@@ -1,5 +1,11 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm, UserCreationForm
|
||||
from django.contrib.auth.forms import (
|
||||
AuthenticationForm,
|
||||
PasswordChangeForm,
|
||||
PasswordResetForm,
|
||||
SetPasswordForm,
|
||||
UserCreationForm,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Los formularios de auth de Django no traen ninguna clase CSS en sus
|
||||
@@ -18,6 +24,14 @@ class StyledAuthenticationForm(AuthenticationForm):
|
||||
|
||||
|
||||
class StyledUserCreationForm(UserCreationForm):
|
||||
# UserCreationForm no pide email por defecto (piensa solo en
|
||||
# usuario/contraseña); sin él, PasswordResetForm no tiene a qué
|
||||
# dirección mandar el enlace de recuperación de estos usuarios.
|
||||
email = forms.EmailField(label=_('Email'))
|
||||
|
||||
class Meta(UserCreationForm.Meta):
|
||||
fields = ('username', 'email')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for field in self.fields.values():
|
||||
@@ -31,6 +45,20 @@ class StyledPasswordChangeForm(PasswordChangeForm):
|
||||
field.widget.attrs['class'] = INPUT_CLASSES
|
||||
|
||||
|
||||
class StyledPasswordResetForm(PasswordResetForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for field in self.fields.values():
|
||||
field.widget.attrs['class'] = INPUT_CLASSES
|
||||
|
||||
|
||||
class StyledSetPasswordForm(SetPasswordForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for field in self.fields.values():
|
||||
field.widget.attrs['class'] = INPUT_CLASSES
|
||||
|
||||
|
||||
class EmailUpdateForm(forms.Form):
|
||||
email = forms.EmailField(label=_('Email'), widget=forms.EmailInput(attrs={'class': INPUT_CLASSES}))
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: crochet\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-20 10:46+0000\n"
|
||||
"POT-Creation-Date: 2026-07-21 10:20+0000\n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: crochet/forms.py:35 crochet/templates/crochet/account_settings.html:22
|
||||
#: crochet/forms.py:30 crochet/forms.py:63
|
||||
#: crochet/templates/crochet/account_settings.html:22
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
@@ -26,6 +27,7 @@ msgstr "Password updated."
|
||||
|
||||
#: crochet/templates/crochet/_account_password_form.html:19
|
||||
#: crochet/templates/crochet/account_settings.html:29
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:30
|
||||
msgid "Cambiar contraseña"
|
||||
msgstr "Change password"
|
||||
|
||||
@@ -57,13 +59,12 @@ msgid "Actualizado el %(date)s"
|
||||
msgstr "Updated on %(date)s"
|
||||
|
||||
#: crochet/templates/crochet/account_home.html:44
|
||||
#: crochet/templates/crochet/pattern.html:158
|
||||
#: crochet/templates/crochet/pattern.html:204
|
||||
#: crochet/templates/crochet/pattern.html:163
|
||||
msgid "Vista previa"
|
||||
msgstr "Preview"
|
||||
|
||||
#: crochet/templates/crochet/account_home.html:48
|
||||
#: crochet/templates/crochet/pattern.html:50
|
||||
#: crochet/templates/crochet/pattern.html:55
|
||||
msgid "Eliminar"
|
||||
msgstr "Delete"
|
||||
|
||||
@@ -91,16 +92,12 @@ msgstr "Log out"
|
||||
|
||||
#: crochet/templates/crochet/base.html:62
|
||||
#: crochet/templates/crochet/home.html:24
|
||||
#: crochet/templates/crochet/password_reset_complete.html:18
|
||||
#: crochet/templates/registration/login.html:4
|
||||
#: crochet/templates/registration/login.html:14
|
||||
msgid "Iniciar sesión"
|
||||
msgstr "Log in"
|
||||
|
||||
#: crochet/templates/crochet/base.html:63
|
||||
#: crochet/templates/registration/register.html:29
|
||||
msgid "Registrarme"
|
||||
msgstr "Sign up"
|
||||
|
||||
#: crochet/templates/crochet/home.html:4
|
||||
msgid "Crochet — Crea y comparte tus patrones"
|
||||
msgstr "Crochet — Create and share your patterns"
|
||||
@@ -161,233 +158,328 @@ msgstr ""
|
||||
"Send your pattern to anyone with a read-only link: no one else will be able "
|
||||
"to edit it."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_complete.html:4
|
||||
#: crochet/templates/crochet/password_reset_complete.html:14
|
||||
msgid "Contraseña actualizada"
|
||||
msgstr "Password updated"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_complete.html:16
|
||||
msgid "Ya puedes iniciar sesión con tu nueva contraseña."
|
||||
msgstr "You can now log in with your new password."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:4
|
||||
msgid "Elegir nueva contraseña"
|
||||
msgstr "Choose new password"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:15
|
||||
msgid "Elige una contraseña nueva"
|
||||
msgstr "Choose a new password"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:33
|
||||
msgid "Enlace no válido"
|
||||
msgstr "Invalid link"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:35
|
||||
msgid ""
|
||||
"El enlace para restablecer la contraseña no es válido, puede que ya se haya "
|
||||
"usado. Solicita uno nuevo."
|
||||
msgstr ""
|
||||
"The password reset link is invalid, possibly because it has already been "
|
||||
"used. Please request a new one."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:38
|
||||
msgid "Solicitar un enlace nuevo"
|
||||
msgstr "Request a new link"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:4
|
||||
#: crochet/templates/crochet/password_reset_form.html:4
|
||||
#: crochet/templates/crochet/password_reset_form.html:14
|
||||
msgid "Recuperar contraseña"
|
||||
msgstr "Reset password"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:14
|
||||
msgid "Revisa tu email"
|
||||
msgstr "Check your email"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:16
|
||||
msgid ""
|
||||
"Si existe una cuenta con ese email, te hemos enviado un enlace para elegir "
|
||||
"una contraseña nueva."
|
||||
msgstr ""
|
||||
"If an account exists with that email, we've sent you a link to choose a new "
|
||||
"password."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:19
|
||||
#: crochet/templates/crochet/password_reset_form.html:32
|
||||
msgid "Volver a iniciar sesión"
|
||||
msgstr "Back to login"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:2
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Has recibido este email porque alguien ha solicitado restablecer la "
|
||||
"contraseña de tu cuenta en %(site_name)s."
|
||||
msgstr ""
|
||||
"You're receiving this email because someone requested a password reset for "
|
||||
"your account at %(site_name)s."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:4
|
||||
msgid "Sigue este enlace para elegir una contraseña nueva:"
|
||||
msgstr "Follow this link to choose a new password:"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:7
|
||||
msgid "Tu usuario, por si lo has olvidado:"
|
||||
msgstr "Your username, in case you've forgotten:"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:9
|
||||
msgid "Si no has solicitado este cambio, puedes ignorar este email."
|
||||
msgstr "If you didn't request this change, you can ignore this email."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_form.html:16
|
||||
msgid ""
|
||||
"Escribe tu email y te enviaremos un enlace para elegir una contraseña nueva."
|
||||
msgstr "Enter your email and we'll send you a link to choose a new password."
|
||||
|
||||
#: crochet/templates/crochet/password_reset_form.html:29
|
||||
msgid "Enviar enlace"
|
||||
msgstr "Send link"
|
||||
|
||||
#: crochet/templates/crochet/password_reset_subject.txt:1
|
||||
#, python-format
|
||||
msgid "Recupera tu contraseña en %(site_name)s"
|
||||
msgstr "Reset your password on %(site_name)s"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:26
|
||||
msgid "Idioma del patrón"
|
||||
msgstr "Pattern language"
|
||||
|
||||
# crochet/templates/crochet/pattern.html
|
||||
#: crochet/templates/crochet/pattern.html:33
|
||||
#: crochet/templates/crochet/pattern.html:38
|
||||
msgid "No se ha podido guardar. Inténtalo de nuevo."
|
||||
msgstr "Couldn't save. Please try again."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:33
|
||||
#: crochet/templates/crochet/pattern.html:38
|
||||
msgid "Guardar"
|
||||
msgstr "Save"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:36
|
||||
#: crochet/templates/crochet/pattern.html:41
|
||||
msgid "Ver patrón"
|
||||
msgstr "View pattern"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:37
|
||||
#: crochet/templates/crochet/pattern.html:42
|
||||
msgid "Exportar a PDF"
|
||||
msgstr "Export to PDF"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:44
|
||||
#: crochet/templates/crochet/pattern.html:49
|
||||
msgid "Más opciones"
|
||||
msgstr "More options"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:60
|
||||
#: crochet/templates/crochet/pattern.html:65
|
||||
msgid "Guardado"
|
||||
msgstr "Saved"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:67
|
||||
#: crochet/templates/crochet/pattern.html:72
|
||||
msgid "Personalización de página"
|
||||
msgstr "Page customization"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:71
|
||||
#: crochet/templates/crochet/pattern.html:76
|
||||
msgid "Título del patrón"
|
||||
msgstr "Pattern title"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:72
|
||||
#: crochet/templates/crochet/pattern.html:77
|
||||
msgid "Título del patrón..."
|
||||
msgstr "Pattern title..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:75
|
||||
#: crochet/templates/crochet/pattern.html:80
|
||||
msgid "Autor"
|
||||
msgstr "Author"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:76
|
||||
#: crochet/templates/crochet/pattern.html:81
|
||||
msgid "Autor..."
|
||||
msgstr "Author..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:81
|
||||
#: crochet/templates/crochet/pattern.html:86
|
||||
msgid "Imagen de portada"
|
||||
msgstr "Cover image"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:84
|
||||
#: crochet/templates/crochet/pattern.html:89
|
||||
msgid "No se ha podido subir la imagen de portada. Inténtalo de nuevo."
|
||||
msgstr "Couldn't upload the cover image. Please try again."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:98
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
#: crochet/templates/crochet/pattern.html:103
|
||||
#: crochet/templates/crochet/pattern.html:187
|
||||
msgid "Color del texto"
|
||||
msgstr "Text color"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:102
|
||||
#: crochet/templates/crochet/pattern.html:107
|
||||
msgid "Color de acento (títulos)"
|
||||
msgstr "Accent color (headings)"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:106
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
#: crochet/templates/crochet/pattern.html:111
|
||||
#: crochet/templates/crochet/pattern.html:187
|
||||
msgid "Color de fondo"
|
||||
msgstr "Background color"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:110
|
||||
#: crochet/templates/crochet/pattern.html:115
|
||||
msgid "Tipografía"
|
||||
msgstr "Font"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:116
|
||||
#: crochet/templates/crochet/pattern.html:121
|
||||
msgid "Monoespaciada"
|
||||
msgstr "Monospace"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:120
|
||||
#: crochet/templates/crochet/pattern.html:125
|
||||
msgid "Tamaño de letra"
|
||||
msgstr "Font size"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:122
|
||||
#: crochet/templates/crochet/pattern.html:127
|
||||
msgid "Pequeño"
|
||||
msgstr "Small"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:123
|
||||
#: crochet/templates/crochet/pattern.html:128
|
||||
msgid "Normal"
|
||||
msgstr "Normal"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:124
|
||||
#: crochet/templates/crochet/pattern.html:129
|
||||
msgid "Grande"
|
||||
msgstr "Large"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:128
|
||||
#: crochet/templates/crochet/pattern.html:133
|
||||
msgid "Alineación del texto"
|
||||
msgstr "Text alignment"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:130
|
||||
#: crochet/templates/crochet/pattern.html:135
|
||||
msgid "Izquierda"
|
||||
msgstr "Left"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:131
|
||||
#: crochet/templates/crochet/pattern.html:136
|
||||
msgid "Centrado"
|
||||
msgstr "Center"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:132
|
||||
#: crochet/templates/crochet/pattern.html:137
|
||||
msgid "Justificado"
|
||||
msgstr "Justify"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:136
|
||||
#: crochet/templates/crochet/pattern.html:141
|
||||
msgid "Tamaño de página"
|
||||
msgstr "Page size"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:141
|
||||
#: crochet/templates/crochet/pattern.html:146
|
||||
msgid "Carta (Letter)"
|
||||
msgstr "Letter"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:146
|
||||
#: crochet/templates/crochet/pattern.html:151
|
||||
msgid "Orientación"
|
||||
msgstr "Orientation"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:148
|
||||
#: crochet/templates/crochet/pattern.html:153
|
||||
msgid "Vertical"
|
||||
msgstr "Portrait"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:149
|
||||
#: crochet/templates/crochet/pattern.html:154
|
||||
msgid "Horizontal"
|
||||
msgstr "Landscape"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:157
|
||||
#: crochet/templates/crochet/pattern.html:165
|
||||
#: crochet/templates/crochet/pattern.html:162
|
||||
#: crochet/templates/crochet/pattern.html:170
|
||||
msgid "Secciones"
|
||||
msgstr "Sections"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:167
|
||||
#: crochet/templates/crochet/pattern.html:172
|
||||
msgid "Colapsar todo"
|
||||
msgstr "Collapse all"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:167
|
||||
#: crochet/templates/crochet/pattern.html:172
|
||||
msgid "Expandir todo"
|
||||
msgstr "Expand all"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:172
|
||||
#: crochet/templates/crochet/pattern.html:177
|
||||
msgid "Añadir sección"
|
||||
msgstr "Add section"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:175
|
||||
#: crochet/templates/crochet/pattern.html:180
|
||||
msgid "Título"
|
||||
msgstr "Title"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:175
|
||||
#: crochet/templates/crochet/pattern.html:180
|
||||
msgid "Título..."
|
||||
msgstr "Title..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:177
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
msgid "Subtítulo"
|
||||
msgstr "Subtitle"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:177
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
msgid "Subtítulo..."
|
||||
msgstr "Subtitle..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:179
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
msgid "Texto"
|
||||
msgstr "Text"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:179
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
msgid "Escribe aquí..."
|
||||
msgstr "Write here..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:181
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
#: crochet/templates/crochet/pattern.html:186
|
||||
#: crochet/templates/crochet/pattern.html:187
|
||||
msgid "Nota"
|
||||
msgstr "Note"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:181
|
||||
#: crochet/templates/crochet/pattern.html:186
|
||||
msgid "Escribe una nota o consejo..."
|
||||
msgstr "Write a note or tip..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
#: crochet/templates/crochet/pattern.html:189
|
||||
msgid "Materiales"
|
||||
msgstr "Materials"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
#: crochet/templates/crochet/pattern.html:189
|
||||
msgid "Añadir material..."
|
||||
msgstr "Add material..."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
#: crochet/templates/crochet/pattern.html:189
|
||||
msgid "Añadir línea"
|
||||
msgstr "Add line"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:186
|
||||
#: crochet/templates/crochet/pattern.html:188
|
||||
#: crochet/templates/crochet/pattern.html:191
|
||||
#: crochet/templates/crochet/pattern.html:193
|
||||
msgid "Imagen"
|
||||
msgstr "Image"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:188
|
||||
#: crochet/templates/crochet/pattern.html:193
|
||||
msgid "No se ha podido subir la imagen. Inténtalo de nuevo."
|
||||
msgstr "Couldn't upload the image. Please try again."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:190
|
||||
#: crochet/templates/crochet/pattern.html:195
|
||||
msgid "Patrón"
|
||||
msgstr "Pattern"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:190
|
||||
#: crochet/templates/crochet/pattern.html:195
|
||||
msgid "Elementos"
|
||||
msgstr "Elements"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:192
|
||||
#: crochet/templates/crochet/pattern.html:197
|
||||
msgid "Grupo"
|
||||
msgstr "Group"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:196
|
||||
#: crochet/templates/crochet/pattern.html:201
|
||||
msgid "Arrastrar para reordenar"
|
||||
msgstr "Drag to reorder"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:197
|
||||
#: crochet/templates/crochet/pattern.html:202
|
||||
msgid "Colapsar / expandir"
|
||||
msgstr "Collapse / expand"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:198
|
||||
#: crochet/templates/crochet/pattern.html:203
|
||||
msgid "Duplicar sección"
|
||||
msgstr "Duplicate section"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:199
|
||||
#: crochet/templates/crochet/pattern.html:204
|
||||
msgid "Eliminar sección"
|
||||
msgstr "Delete section"
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:200
|
||||
#: crochet/templates/crochet/pattern.html:205
|
||||
msgid ""
|
||||
"¿Seguro que quieres eliminar este grupo? Se eliminarán también todas las "
|
||||
"secciones que contiene."
|
||||
@@ -395,7 +487,7 @@ msgstr ""
|
||||
"Are you sure you want to delete this group? All sections inside it will also "
|
||||
"be deleted."
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:206
|
||||
#: crochet/templates/crochet/pattern.html:210
|
||||
msgid "Añade una sección para ver aquí el resultado."
|
||||
msgstr "Add a section to see the result here."
|
||||
|
||||
@@ -412,6 +504,10 @@ msgid "Entrar"
|
||||
msgstr "Log in"
|
||||
|
||||
#: crochet/templates/registration/login.html:32
|
||||
msgid "¿Has olvidado tu contraseña?"
|
||||
msgstr "Forgot your password?"
|
||||
|
||||
#: crochet/templates/registration/login.html:35
|
||||
msgid "¿No tienes cuenta? Regístrate"
|
||||
msgstr "Don't have an account? Sign up"
|
||||
|
||||
@@ -420,6 +516,10 @@ msgstr "Don't have an account? Sign up"
|
||||
msgid "Crear cuenta"
|
||||
msgstr "Create account"
|
||||
|
||||
#: crochet/templates/registration/register.html:29
|
||||
msgid "Registrarme"
|
||||
msgstr "Sign up"
|
||||
|
||||
#: crochet/templates/registration/register.html:32
|
||||
msgid "¿Ya tienes cuenta? Inicia sesión"
|
||||
msgstr "Already have an account? Log in"
|
||||
@@ -484,6 +584,22 @@ msgstr ""
|
||||
msgid "account/logout/"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/urls.py:60
|
||||
msgid "account/password-reset/"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/urls.py:75
|
||||
msgid "account/password-reset/done/"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/urls.py:79
|
||||
msgid "account/reset/<uidb64>/<token>/"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/urls.py:87
|
||||
msgid "account/reset/done/"
|
||||
msgstr ""
|
||||
|
||||
# crochet/views.py (PatternDetailView.EMPTY_MESSAGE)
|
||||
#: crochet/views.py:24
|
||||
msgid "Este patrón todavía no tiene contenido."
|
||||
|
||||
@@ -2,13 +2,14 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: crochet\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-20 10:46+0000\n"
|
||||
"POT-Creation-Date: 2026-07-21 10:20+0000\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: crochet/forms.py:35 crochet/templates/crochet/account_settings.html:22
|
||||
#: crochet/forms.py:30 crochet/forms.py:63
|
||||
#: crochet/templates/crochet/account_settings.html:22
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
@@ -26,6 +27,7 @@ msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/_account_password_form.html:19
|
||||
#: crochet/templates/crochet/account_settings.html:29
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:30
|
||||
msgid "Cambiar contraseña"
|
||||
msgstr ""
|
||||
|
||||
@@ -57,13 +59,12 @@ msgid "Actualizado el %(date)s"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/account_home.html:44
|
||||
#: crochet/templates/crochet/pattern.html:158
|
||||
#: crochet/templates/crochet/pattern.html:204
|
||||
#: crochet/templates/crochet/pattern.html:163
|
||||
msgid "Vista previa"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/account_home.html:48
|
||||
#: crochet/templates/crochet/pattern.html:50
|
||||
#: crochet/templates/crochet/pattern.html:55
|
||||
msgid "Eliminar"
|
||||
msgstr ""
|
||||
|
||||
@@ -91,16 +92,12 @@ msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/base.html:62
|
||||
#: crochet/templates/crochet/home.html:24
|
||||
#: crochet/templates/crochet/password_reset_complete.html:18
|
||||
#: crochet/templates/registration/login.html:4
|
||||
#: crochet/templates/registration/login.html:14
|
||||
msgid "Iniciar sesión"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/base.html:63
|
||||
#: crochet/templates/registration/register.html:29
|
||||
msgid "Registrarme"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/home.html:4
|
||||
msgid "Crochet — Crea y comparte tus patrones"
|
||||
msgstr ""
|
||||
@@ -153,238 +150,327 @@ msgid ""
|
||||
"podrá editarlo."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:33
|
||||
#: crochet/templates/crochet/password_reset_complete.html:4
|
||||
#: crochet/templates/crochet/password_reset_complete.html:14
|
||||
msgid "Contraseña actualizada"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_complete.html:16
|
||||
msgid "Ya puedes iniciar sesión con tu nueva contraseña."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:4
|
||||
msgid "Elegir nueva contraseña"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:15
|
||||
msgid "Elige una contraseña nueva"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:33
|
||||
msgid "Enlace no válido"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:35
|
||||
msgid ""
|
||||
"El enlace para restablecer la contraseña no es válido, puede que ya se haya "
|
||||
"usado. Solicita uno nuevo."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_confirm.html:38
|
||||
msgid "Solicitar un enlace nuevo"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:4
|
||||
#: crochet/templates/crochet/password_reset_form.html:4
|
||||
#: crochet/templates/crochet/password_reset_form.html:14
|
||||
msgid "Recuperar contraseña"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:14
|
||||
msgid "Revisa tu email"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:16
|
||||
msgid ""
|
||||
"Si existe una cuenta con ese email, te hemos enviado un enlace para elegir "
|
||||
"una contraseña nueva."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_done.html:19
|
||||
#: crochet/templates/crochet/password_reset_form.html:32
|
||||
msgid "Volver a iniciar sesión"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:2
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Has recibido este email porque alguien ha solicitado restablecer la "
|
||||
"contraseña de tu cuenta en %(site_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:4
|
||||
msgid "Sigue este enlace para elegir una contraseña nueva:"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:7
|
||||
msgid "Tu usuario, por si lo has olvidado:"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_email.html:9
|
||||
msgid "Si no has solicitado este cambio, puedes ignorar este email."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_form.html:16
|
||||
msgid ""
|
||||
"Escribe tu email y te enviaremos un enlace para elegir una contraseña nueva."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_form.html:29
|
||||
msgid "Enviar enlace"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/password_reset_subject.txt:1
|
||||
#, python-format
|
||||
msgid "Recupera tu contraseña en %(site_name)s"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:26
|
||||
msgid "Idioma del patrón"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:38
|
||||
msgid "No se ha podido guardar. Inténtalo de nuevo."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:33
|
||||
#: crochet/templates/crochet/pattern.html:38
|
||||
msgid "Guardar"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:36
|
||||
#: crochet/templates/crochet/pattern.html:41
|
||||
msgid "Ver patrón"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:37
|
||||
#: crochet/templates/crochet/pattern.html:42
|
||||
msgid "Exportar a PDF"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:44
|
||||
#: crochet/templates/crochet/pattern.html:49
|
||||
msgid "Más opciones"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:60
|
||||
#: crochet/templates/crochet/pattern.html:65
|
||||
msgid "Guardado"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:67
|
||||
#: crochet/templates/crochet/pattern.html:72
|
||||
msgid "Personalización de página"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:71
|
||||
#: crochet/templates/crochet/pattern.html:76
|
||||
msgid "Título del patrón"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:72
|
||||
#: crochet/templates/crochet/pattern.html:77
|
||||
msgid "Título del patrón..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:75
|
||||
#: crochet/templates/crochet/pattern.html:80
|
||||
msgid "Autor"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:76
|
||||
#: crochet/templates/crochet/pattern.html:81
|
||||
msgid "Autor..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:81
|
||||
#: crochet/templates/crochet/pattern.html:86
|
||||
msgid "Imagen de portada"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:84
|
||||
#: crochet/templates/crochet/pattern.html:89
|
||||
msgid "No se ha podido subir la imagen de portada. Inténtalo de nuevo."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:98
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
#: crochet/templates/crochet/pattern.html:103
|
||||
#: crochet/templates/crochet/pattern.html:187
|
||||
msgid "Color del texto"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:102
|
||||
#: crochet/templates/crochet/pattern.html:107
|
||||
msgid "Color de acento (títulos)"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:106
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
#: crochet/templates/crochet/pattern.html:111
|
||||
#: crochet/templates/crochet/pattern.html:187
|
||||
msgid "Color de fondo"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:110
|
||||
#: crochet/templates/crochet/pattern.html:115
|
||||
msgid "Tipografía"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:116
|
||||
#: crochet/templates/crochet/pattern.html:121
|
||||
msgid "Monoespaciada"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:120
|
||||
#: crochet/templates/crochet/pattern.html:125
|
||||
msgid "Tamaño de letra"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:122
|
||||
#: crochet/templates/crochet/pattern.html:127
|
||||
msgid "Pequeño"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:123
|
||||
#: crochet/templates/crochet/pattern.html:128
|
||||
msgid "Normal"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:124
|
||||
#: crochet/templates/crochet/pattern.html:129
|
||||
msgid "Grande"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:128
|
||||
#: crochet/templates/crochet/pattern.html:133
|
||||
msgid "Alineación del texto"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:130
|
||||
#: crochet/templates/crochet/pattern.html:135
|
||||
msgid "Izquierda"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:131
|
||||
#: crochet/templates/crochet/pattern.html:136
|
||||
msgid "Centrado"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:132
|
||||
#: crochet/templates/crochet/pattern.html:137
|
||||
msgid "Justificado"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:136
|
||||
#: crochet/templates/crochet/pattern.html:141
|
||||
msgid "Tamaño de página"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:141
|
||||
#: crochet/templates/crochet/pattern.html:146
|
||||
msgid "Carta (Letter)"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:146
|
||||
#: crochet/templates/crochet/pattern.html:151
|
||||
msgid "Orientación"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:148
|
||||
#: crochet/templates/crochet/pattern.html:153
|
||||
msgid "Vertical"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:149
|
||||
#: crochet/templates/crochet/pattern.html:154
|
||||
msgid "Horizontal"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:157
|
||||
#: crochet/templates/crochet/pattern.html:165
|
||||
#: crochet/templates/crochet/pattern.html:162
|
||||
#: crochet/templates/crochet/pattern.html:170
|
||||
msgid "Secciones"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:167
|
||||
#: crochet/templates/crochet/pattern.html:172
|
||||
msgid "Colapsar todo"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:167
|
||||
#: crochet/templates/crochet/pattern.html:172
|
||||
msgid "Expandir todo"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:172
|
||||
#: crochet/templates/crochet/pattern.html:177
|
||||
msgid "Añadir sección"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:175
|
||||
#: crochet/templates/crochet/pattern.html:180
|
||||
msgid "Título"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:175
|
||||
#: crochet/templates/crochet/pattern.html:180
|
||||
msgid "Título..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:177
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
msgid "Subtítulo"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:177
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
msgid "Subtítulo..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:179
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
msgid "Texto"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:179
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
msgid "Escribe aquí..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:181
|
||||
#: crochet/templates/crochet/pattern.html:182
|
||||
#: crochet/templates/crochet/pattern.html:186
|
||||
#: crochet/templates/crochet/pattern.html:187
|
||||
msgid "Nota"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:181
|
||||
#: crochet/templates/crochet/pattern.html:186
|
||||
msgid "Escribe una nota o consejo..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
#: crochet/templates/crochet/pattern.html:189
|
||||
msgid "Materiales"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
#: crochet/templates/crochet/pattern.html:189
|
||||
msgid "Añadir material..."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:184
|
||||
#: crochet/templates/crochet/pattern.html:189
|
||||
msgid "Añadir línea"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:186
|
||||
#: crochet/templates/crochet/pattern.html:188
|
||||
#: crochet/templates/crochet/pattern.html:191
|
||||
#: crochet/templates/crochet/pattern.html:193
|
||||
msgid "Imagen"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:188
|
||||
#: crochet/templates/crochet/pattern.html:193
|
||||
msgid "No se ha podido subir la imagen. Inténtalo de nuevo."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:190
|
||||
#: crochet/templates/crochet/pattern.html:195
|
||||
msgid "Patrón"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:190
|
||||
#: crochet/templates/crochet/pattern.html:195
|
||||
msgid "Elementos"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:192
|
||||
#: crochet/templates/crochet/pattern.html:197
|
||||
msgid "Grupo"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:196
|
||||
#: crochet/templates/crochet/pattern.html:201
|
||||
msgid "Arrastrar para reordenar"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:197
|
||||
#: crochet/templates/crochet/pattern.html:202
|
||||
msgid "Colapsar / expandir"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:198
|
||||
#: crochet/templates/crochet/pattern.html:203
|
||||
msgid "Duplicar sección"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:199
|
||||
#: crochet/templates/crochet/pattern.html:204
|
||||
msgid "Eliminar sección"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:200
|
||||
#: crochet/templates/crochet/pattern.html:205
|
||||
msgid ""
|
||||
"¿Seguro que quieres eliminar este grupo? Se eliminarán también todas las "
|
||||
"secciones que contiene."
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/crochet/pattern.html:206
|
||||
#: crochet/templates/crochet/pattern.html:210
|
||||
msgid "Añade una sección para ver aquí el resultado."
|
||||
msgstr ""
|
||||
|
||||
@@ -401,6 +487,10 @@ msgid "Entrar"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/registration/login.html:32
|
||||
msgid "¿Has olvidado tu contraseña?"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/registration/login.html:35
|
||||
msgid "¿No tienes cuenta? Regístrate"
|
||||
msgstr ""
|
||||
|
||||
@@ -409,6 +499,10 @@ msgstr ""
|
||||
msgid "Crear cuenta"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/registration/register.html:29
|
||||
msgid "Registrarme"
|
||||
msgstr ""
|
||||
|
||||
#: crochet/templates/registration/register.html:32
|
||||
msgid "¿Ya tienes cuenta? Inicia sesión"
|
||||
msgstr ""
|
||||
@@ -476,6 +570,22 @@ msgstr "cuenta/entrar/"
|
||||
msgid "account/logout/"
|
||||
msgstr "cuenta/salir/"
|
||||
|
||||
#: crochet/urls.py:60
|
||||
msgid "account/password-reset/"
|
||||
msgstr "cuenta/recuperar-contrasena/"
|
||||
|
||||
#: crochet/urls.py:75
|
||||
msgid "account/password-reset/done/"
|
||||
msgstr "cuenta/recuperar-contrasena/enviado/"
|
||||
|
||||
#: crochet/urls.py:79
|
||||
msgid "account/reset/<uidb64>/<token>/"
|
||||
msgstr "cuenta/restablecer/<uidb64>/<token>/"
|
||||
|
||||
#: crochet/urls.py:87
|
||||
msgid "account/reset/done/"
|
||||
msgstr "cuenta/restablecer/hecho/"
|
||||
|
||||
#: crochet/views.py:24
|
||||
msgid "Este patrón todavía no tiene contenido."
|
||||
msgstr ""
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{% extends 'crochet/base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Contraseña actualizada' %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'crochet/_platform_assets.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="flex justify-center px-4 py-12">
|
||||
<div class="card w-full max-w-sm bg-base-100 shadow-xl border border-base-300">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">{% trans 'Contraseña actualizada' %}</h1>
|
||||
<p class="text-sm text-base-content/70">
|
||||
{% trans 'Ya puedes iniciar sesión con tu nueva contraseña.' %}
|
||||
</p>
|
||||
<a href="{% url 'crochet:login' %}" class="btn btn-primary mt-2">{% trans 'Iniciar sesión' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,44 @@
|
||||
{% extends 'crochet/base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Elegir nueva contraseña' %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'crochet/_platform_assets.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="flex justify-center px-4 py-12">
|
||||
<div class="card w-full max-w-sm bg-base-100 shadow-xl border border-base-300">
|
||||
<div class="card-body">
|
||||
{% if validlink %}
|
||||
<h1 class="card-title">{% trans 'Elige una contraseña nueva' %}</h1>
|
||||
<form method="post" class="flex flex-col gap-3">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-sm">{{ field.label }}</span>
|
||||
{{ field }}
|
||||
{% for error in field.errors %}
|
||||
<span class="text-error text-xs">{{ error }}</span>
|
||||
{% endfor %}
|
||||
{% if field.help_text %}
|
||||
<span class="text-xs text-base-content/60">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endfor %}
|
||||
<button type="submit" class="btn btn-primary mt-2">{% trans 'Cambiar contraseña' %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<h1 class="card-title">{% trans 'Enlace no válido' %}</h1>
|
||||
<p class="text-sm text-base-content/70">
|
||||
{% trans 'El enlace para restablecer la contraseña no es válido, puede que ya se haya usado. Solicita uno nuevo.' %}
|
||||
</p>
|
||||
<p class="text-sm text-center mt-2">
|
||||
<a href="{% url 'crochet:password_reset' %}" class="link link-primary">{% trans 'Solicitar un enlace nuevo' %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends 'crochet/base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Recuperar contraseña' %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'crochet/_platform_assets.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="flex justify-center px-4 py-12">
|
||||
<div class="card w-full max-w-sm bg-base-100 shadow-xl border border-base-300">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">{% trans 'Revisa tu email' %}</h1>
|
||||
<p class="text-sm text-base-content/70">
|
||||
{% trans 'Si existe una cuenta con ese email, te hemos enviado un enlace para elegir una contraseña nueva.' %}
|
||||
</p>
|
||||
<p class="text-sm text-center mt-2">
|
||||
<a href="{% url 'crochet:login' %}" class="link link-primary">{% trans 'Volver a iniciar sesión' %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}Has recibido este email porque alguien ha solicitado restablecer la contraseña de tu cuenta en {{ site_name }}.{% endblocktrans %}
|
||||
|
||||
{% trans 'Sigue este enlace para elegir una contraseña nueva:' %}
|
||||
{{ protocol }}://{{ domain }}{% url 'crochet:password_reset_confirm' uidb64=uid token=token %}
|
||||
|
||||
{% trans 'Tu usuario, por si lo has olvidado:' %} {{ user.get_username }}
|
||||
|
||||
{% trans 'Si no has solicitado este cambio, puedes ignorar este email.' %}
|
||||
{% endautoescape %}
|
||||
@@ -0,0 +1,37 @@
|
||||
{% extends 'crochet/base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Recuperar contraseña' %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% include 'crochet/_platform_assets.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="flex justify-center px-4 py-12">
|
||||
<div class="card w-full max-w-sm bg-base-100 shadow-xl border border-base-300">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">{% trans 'Recuperar contraseña' %}</h1>
|
||||
<p class="text-sm text-base-content/70">
|
||||
{% trans 'Escribe tu email y te enviaremos un enlace para elegir una contraseña nueva.' %}
|
||||
</p>
|
||||
<form method="post" class="flex flex-col gap-3">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-sm">{{ field.label }}</span>
|
||||
{{ field }}
|
||||
{% for error in field.errors %}
|
||||
<span class="text-error text-xs">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</label>
|
||||
{% endfor %}
|
||||
<button type="submit" class="btn btn-primary mt-2">{% trans 'Enviar enlace' %}</button>
|
||||
</form>
|
||||
<p class="text-sm text-center mt-2">
|
||||
<a href="{% url 'crochet:login' %}" class="link link-primary">{% trans 'Volver a iniciar sesión' %}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{% load i18n %}{% blocktrans %}Recupera tu contraseña en {{ site_name }}{% endblocktrans %}
|
||||
@@ -29,6 +29,9 @@
|
||||
<button type="submit" class="btn btn-primary mt-2">{% trans 'Entrar' %}</button>
|
||||
</form>
|
||||
<p class="text-sm text-center mt-2">
|
||||
<a href="{% url 'crochet:password_reset' %}" class="link link-primary">{% trans '¿Has olvidado tu contraseña?' %}</a>
|
||||
</p>
|
||||
<p class="text-sm text-center">
|
||||
<a href="{% url 'crochet:register' %}" class="link link-primary">{% trans '¿No tienes cuenta? Regístrate' %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -20,17 +20,20 @@ class RegisterViewTests(TestCase):
|
||||
def test_post_creates_user_and_logs_in(self):
|
||||
response = self.client.post(self.url, {
|
||||
'username': 'crocheter',
|
||||
'email': 'crocheter@example.com',
|
||||
'password1': 'a-very-uncommon-pw-1',
|
||||
'password2': 'a-very-uncommon-pw-1',
|
||||
})
|
||||
|
||||
self.assertTrue(User.objects.filter(username='crocheter').exists())
|
||||
user = User.objects.get(username='crocheter')
|
||||
self.assertEqual(user.email, 'crocheter@example.com')
|
||||
self.assertRedirects(response, reverse('crochet:account_home'))
|
||||
self.assertIn('_auth_user_id', self.client.session)
|
||||
|
||||
def test_post_with_mismatched_passwords_does_not_create_user(self):
|
||||
response = self.client.post(self.url, {
|
||||
'username': 'crocheter',
|
||||
'email': 'crocheter@example.com',
|
||||
'password1': 'a-very-uncommon-pw-1',
|
||||
'password2': 'something-else',
|
||||
})
|
||||
@@ -38,6 +41,18 @@ class RegisterViewTests(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(User.objects.filter(username='crocheter').exists())
|
||||
|
||||
def test_post_without_email_does_not_create_user(self):
|
||||
# Sin email no habría a dónde mandar el enlace de recuperación de
|
||||
# contraseña (ver PasswordResetForm/StyledUserCreationForm).
|
||||
response = self.client.post(self.url, {
|
||||
'username': 'crocheter',
|
||||
'password1': 'a-very-uncommon-pw-1',
|
||||
'password2': 'a-very-uncommon-pw-1',
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(User.objects.filter(username='crocheter').exists())
|
||||
|
||||
|
||||
class LoginViewTests(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.core import mail
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import translation
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
|
||||
|
||||
class PasswordResetRequestViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(
|
||||
username='crocheter', password='the-old-password-1', email='crocheter@example.com',
|
||||
)
|
||||
with translation.override('es'):
|
||||
self.url = reverse('crochet:password_reset')
|
||||
|
||||
def test_get_renders_the_form(self):
|
||||
response = self.client.get(self.url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'crochet/password_reset_form.html')
|
||||
|
||||
def test_known_email_sends_a_reset_link(self):
|
||||
response = self.client.post(self.url, {'email': 'crocheter@example.com'})
|
||||
|
||||
with translation.override('es'):
|
||||
done_url = reverse('crochet:password_reset_done')
|
||||
self.assertRedirects(response, done_url)
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertIn('crocheter@example.com', mail.outbox[0].to)
|
||||
self.assertIn('http://testserver', mail.outbox[0].body)
|
||||
|
||||
def test_unknown_email_does_not_send_anything_but_still_redirects(self):
|
||||
# Ni la vista ni la plantilla deben delatar si el email existe o no
|
||||
# en el sistema (comportamiento por defecto de PasswordResetForm):
|
||||
# misma redirección tanto si existe como si no, para no permitir
|
||||
# enumerar cuentas registradas probando emails.
|
||||
response = self.client.post(self.url, {'email': 'nobody@example.com'})
|
||||
|
||||
with translation.override('es'):
|
||||
done_url = reverse('crochet:password_reset_done')
|
||||
self.assertRedirects(response, done_url)
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
||||
|
||||
class PasswordResetDoneViewTests(TestCase):
|
||||
def test_get_renders_the_confirmation_message(self):
|
||||
with translation.override('es'):
|
||||
url = reverse('crochet:password_reset_done')
|
||||
response = self.client.get(url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'crochet/password_reset_done.html')
|
||||
|
||||
|
||||
class PasswordResetConfirmViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(
|
||||
username='crocheter', password='the-old-password-1', email='crocheter@example.com',
|
||||
)
|
||||
uidb64 = urlsafe_base64_encode(force_bytes(self.user.pk))
|
||||
token = default_token_generator.make_token(self.user)
|
||||
with translation.override('es'):
|
||||
self.confirm_url = reverse(
|
||||
'crochet:password_reset_confirm', kwargs={'uidb64': uidb64, 'token': token},
|
||||
)
|
||||
|
||||
def test_valid_link_allows_setting_a_new_password(self):
|
||||
# PasswordResetConfirmView consume el token de la URL en el primer
|
||||
# GET (redirige a una URL "set-password" en sesión) precisamente
|
||||
# para que no quede reutilizable ni visible en el historial del
|
||||
# navegador; hay que seguir esa redirección antes de poder enviar
|
||||
# el formulario.
|
||||
get_response = self.client.get(self.confirm_url, follow=True)
|
||||
self.assertContains(get_response, 'id_new_password1')
|
||||
|
||||
response = self.client.post(get_response.redirect_chain[-1][0], {
|
||||
'new_password1': 'the-new-password-2',
|
||||
'new_password2': 'the-new-password-2',
|
||||
})
|
||||
|
||||
with translation.override('es'):
|
||||
complete_url = reverse('crochet:password_reset_complete')
|
||||
self.assertRedirects(response, complete_url)
|
||||
self.user.refresh_from_db()
|
||||
self.assertTrue(self.user.check_password('the-new-password-2'))
|
||||
|
||||
def test_mismatched_passwords_do_not_change_the_password(self):
|
||||
get_response = self.client.get(self.confirm_url, follow=True)
|
||||
|
||||
response = self.client.post(get_response.redirect_chain[-1][0], {
|
||||
'new_password1': 'the-new-password-2',
|
||||
'new_password2': 'something-else-3',
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'class="text-error text-xs"')
|
||||
self.user.refresh_from_db()
|
||||
self.assertTrue(self.user.check_password('the-old-password-1'))
|
||||
|
||||
def test_invalid_token_shows_the_invalid_link_message(self):
|
||||
uidb64 = urlsafe_base64_encode(force_bytes(self.user.pk))
|
||||
with translation.override('es'):
|
||||
bogus_url = reverse(
|
||||
'crochet:password_reset_confirm', kwargs={'uidb64': uidb64, 'token': 'not-a-real-token'},
|
||||
)
|
||||
|
||||
response = self.client.get(bogus_url, follow=True)
|
||||
|
||||
self.assertContains(response, 'Enlace no válido')
|
||||
self.assertNotContains(response, 'id_new_password1')
|
||||
|
||||
|
||||
class PasswordResetCompleteViewTests(TestCase):
|
||||
def test_get_renders_the_success_message(self):
|
||||
with translation.override('es'):
|
||||
url = reverse('crochet:password_reset_complete')
|
||||
response = self.client.get(url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'crochet/password_reset_complete.html')
|
||||
|
||||
|
||||
class LoginPageForgotPasswordLinkTests(TestCase):
|
||||
def test_login_page_links_to_password_reset(self):
|
||||
with translation.override('es'):
|
||||
login_url = reverse('crochet:login')
|
||||
reset_url = reverse('crochet:password_reset')
|
||||
|
||||
response = self.client.get(login_url)
|
||||
|
||||
self.assertContains(response, f'href="{reset_url}"')
|
||||
+33
-2
@@ -1,8 +1,8 @@
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.urls import path
|
||||
from django.urls import path, reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from crochet.forms import StyledAuthenticationForm
|
||||
from crochet.forms import StyledAuthenticationForm, StyledPasswordResetForm, StyledSetPasswordForm
|
||||
from crochet.views import (
|
||||
AccountEmailUpdateView,
|
||||
AccountHomeView,
|
||||
@@ -56,4 +56,35 @@ urlpatterns = [
|
||||
),
|
||||
),
|
||||
path(_('account/logout/'), name='logout', view=auth_views.LogoutView.as_view()),
|
||||
path(
|
||||
_('account/password-reset/'), name='password_reset',
|
||||
view=auth_views.PasswordResetView.as_view(
|
||||
# No en registration/ (a diferencia de login.html/register.html):
|
||||
# django.contrib.admin trae sus propias plantillas con estos
|
||||
# mismos nombres bajo registration/ y, al ir antes que crochet
|
||||
# en INSTALLED_APPS, el buscador de plantillas por APP_DIRS
|
||||
# encontraría siempre las suyas primero, tapando las nuestras.
|
||||
template_name='crochet/password_reset_form.html',
|
||||
email_template_name='crochet/password_reset_email.html',
|
||||
subject_template_name='crochet/password_reset_subject.txt',
|
||||
form_class=StyledPasswordResetForm,
|
||||
success_url=reverse_lazy('crochet:password_reset_done'),
|
||||
),
|
||||
),
|
||||
path(
|
||||
_('account/password-reset/done/'), name='password_reset_done',
|
||||
view=auth_views.PasswordResetDoneView.as_view(template_name='crochet/password_reset_done.html'),
|
||||
),
|
||||
path(
|
||||
_('account/reset/<uidb64>/<token>/'), name='password_reset_confirm',
|
||||
view=auth_views.PasswordResetConfirmView.as_view(
|
||||
template_name='crochet/password_reset_confirm.html',
|
||||
form_class=StyledSetPasswordForm,
|
||||
success_url=reverse_lazy('crochet:password_reset_complete'),
|
||||
),
|
||||
),
|
||||
path(
|
||||
_('account/reset/done/'), name='password_reset_complete',
|
||||
view=auth_views.PasswordResetCompleteView.as_view(template_name='crochet/password_reset_complete.html'),
|
||||
),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user