From 80bc3797ffd805d7fb1052b8a0e705a5253a765e Mon Sep 17 00:00:00 2001 From: Pablo Moreno Date: Thu, 16 Jan 2025 23:32:03 +0100 Subject: [PATCH] feat: reset password working --- config/settings/base.py | 5 + config/settings/environ.py | 8 + locale/es_ES/LC_MESSAGES/django.po | 937 +++++++++++------- ...rder_options_order_amount_paid_and_more.py | 3 +- shop/utils.py | 2 +- shop/views.py | 2 +- users/forms/register.py | 2 +- users/forms/reset_password.py | 38 + web/templates/mails/reset_password.html | 14 + web/templates/mails/reset_password.txt | 1 + .../mails/subject_reset_password.txt | 0 web/templates/users/login.html | 29 +- web/templates/users/reset_password.html | 78 +- .../users/reset_password_confirm.html | 50 + .../users/reset_password_email_sent.html | 31 + web/tests/test_manifest.py | 5 +- web/tests/test_orders.py | 2 +- web/urls.py | 22 +- web/views/users.py | 117 ++- web/views/web.py | 2 +- 20 files changed, 932 insertions(+), 416 deletions(-) create mode 100644 web/templates/mails/reset_password.html create mode 100644 web/templates/mails/reset_password.txt create mode 100644 web/templates/mails/subject_reset_password.txt create mode 100644 web/templates/users/reset_password_confirm.html create mode 100644 web/templates/users/reset_password_email_sent.html diff --git a/config/settings/base.py b/config/settings/base.py index 96df0ee..4a0c2dc 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -191,6 +191,11 @@ SPECTACULAR_SETTINGS = { "SERVE_INCLUDE_SCHEMA": False, } + +EMAIL_USE_TLS = True +EMAIL_USE_SSL = False + + CELERY_BROKER_URL = env.str("CELERY_BROKER_URL", default="memory://localhost:8000//") CELERY_TIME_ZONE = TIME_ZONE CELERY_ALWAYS_EAGER = DEBUG diff --git a/config/settings/environ.py b/config/settings/environ.py index 88aa282..3d9c6be 100644 --- a/config/settings/environ.py +++ b/config/settings/environ.py @@ -57,3 +57,11 @@ REDSYS_TPV_DOMAIN = env.str("REDSYS_TPV_DOMAIN", "") ITEMS_PER_PAGE = env.int("ITEMS_PER_PAGE", 20) SHOP_COUNTRY = env.str("SHOP_COUNTRY", "ESPAÑA") NPM_BIN_PATH = which("npm") + +EMAIL_BACKEND = env.str( + "EMAIL_BACKEND", "django.core.mail.backends.console.EmailBackend" +) +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) diff --git a/locale/es_ES/LC_MESSAGES/django.po b/locale/es_ES/LC_MESSAGES/django.po index 66d4f9a..1451319 100644 --- a/locale/es_ES/LC_MESSAGES/django.po +++ b/locale/es_ES/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-01-08 11:47+0100\n" +"POT-Creation-Date: 2025-01-16 23:18+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -33,556 +33,603 @@ msgstr "Etiquetas" msgid "Categorías" msgstr "Categorías" -#: shop/models.py:16 shop/models.py:426 +#: shop/models.py:19 shop/models.py:481 msgid "fecha de creación" msgstr "fecha de creación" -#: shop/models.py:19 +#: shop/models.py:22 msgid "fecha de última modificación" msgstr "fecha de última modificación" -#: shop/models.py:33 +#: shop/models.py:36 msgid "etiqueta" msgstr "etiqueta" -#: shop/models.py:34 shop/models.py:106 +#: shop/models.py:37 shop/models.py:109 msgid "etiquetas" msgstr "etiquetas" -#: shop/models.py:45 +#: shop/models.py:48 msgid "categoría padre" msgstr "categoría padre" -#: shop/models.py:47 +#: shop/models.py:50 msgid "promocionada" msgstr "promocionada" -#: shop/models.py:48 +#: shop/models.py:51 msgid "oculta" msgstr "oculta" -#: shop/models.py:50 +#: shop/models.py:53 msgid "mostrar en cabecera" msgstr "mostrar en cabecera" -#: shop/models.py:56 +#: shop/models.py:59 msgid "slug" msgstr "slug" -#: shop/models.py:69 +#: shop/models.py:72 msgid "categoría" msgstr "categoría" -#: shop/models.py:70 shop/models.py:108 +#: shop/models.py:73 shop/models.py:111 msgid "categorías" msgstr "categorías" -#: shop/models.py:80 +#: shop/models.py:83 msgid "código de referencia" msgstr "código de referencia" -#: shop/models.py:86 shop/models.py:235 shop/models.py:262 shop/models.py:511 +#: shop/models.py:89 shop/models.py:241 shop/models.py:268 shop/models.py:566 msgid "nombre" msgstr "nombre" -#: shop/models.py:91 +#: shop/models.py:94 msgid "descripción" msgstr "descripción" -#: shop/models.py:97 +#: shop/models.py:100 msgid "stock" msgstr "stock" -#: shop/models.py:100 +#: shop/models.py:103 msgid "es un activo digital" msgstr "es un activo digital" -#: shop/models.py:102 +#: shop/models.py:105 msgid "URL de descarga" msgstr "URL de descarga" -#: shop/models.py:104 +#: shop/models.py:107 msgid "imágenes" msgstr "imágenes" -#: shop/models.py:115 shop/models.py:266 +#: shop/models.py:118 shop/models.py:272 msgid "marca" msgstr "marca" -#: shop/models.py:118 +#: shop/models.py:121 msgid "Slug" msgstr "Slug" -#: shop/models.py:120 shop/models.py:500 +#: shop/models.py:123 shop/models.py:555 msgid "oculto" msgstr "oculto" -#: shop/models.py:135 shop/models.py:148 shop/models.py:184 shop/models.py:282 -#: shop/models.py:442 shop/models.py:529 +#: shop/models.py:125 +#, fuzzy +#| msgid "Direcciones de envío" +msgid "es forma de envío" +msgstr "es forma de envío" + +#: shop/models.py:141 shop/models.py:154 shop/models.py:190 shop/models.py:288 +#: shop/models.py:497 shop/models.py:584 msgid "producto" msgstr "producto" -#: shop/models.py:136 +#: shop/models.py:142 msgid "productos" msgstr "productos" -#: shop/models.py:142 shop/models.py:295 +#: shop/models.py:148 shop/models.py:301 msgid "precio" msgstr "precio" -#: shop/models.py:144 +#: shop/models.py:150 msgid "fecha" msgstr "fecha" -#: shop/models.py:154 +#: shop/models.py:160 msgid "impuesto aplicable" msgstr "impuesto aplicable" -#: shop/models.py:161 +#: shop/models.py:167 #, fuzzy #| msgid "total sin impuestos" msgid "precio con impuestos" msgstr "total sin impuestos" -#: shop/models.py:163 +#: shop/models.py:169 msgid "es el precio actual" msgstr "es el precio actual" -#: shop/models.py:174 shop/models.py:175 +#: shop/models.py:180 shop/models.py:181 msgid "precio de producto" msgstr "precio de producto" -#: shop/models.py:181 +#: shop/models.py:187 msgid "código" msgstr "código" -#: shop/models.py:190 shop/models.py:290 shop/models.py:444 +#: shop/models.py:196 shop/models.py:296 shop/models.py:499 msgid "cantidad" msgstr "cantidad" -#: shop/models.py:193 +#: shop/models.py:199 msgid "fecha de caducidad / consumo preferente" msgstr "fecha de caducidad / consumo preferente" -#: shop/models.py:200 shop/models.py:256 +#: shop/models.py:206 shop/models.py:262 msgid "proveedor" msgstr "proveedor" -#: shop/models.py:207 +#: shop/models.py:213 msgid "remesa de producto" msgstr "remesa de producto" -#: shop/models.py:208 +#: shop/models.py:214 msgid "remesas de productos" msgstr "remesas de productos" -#: shop/models.py:213 +#: shop/models.py:219 msgid "código de impuesto" msgstr "código de impuesto" -#: shop/models.py:216 +#: shop/models.py:222 msgid "valor entero (porcentaje)" msgstr "valor entero (porcentaje)" -#: shop/models.py:223 +#: shop/models.py:229 msgid "impuesto" msgstr "impuesto" -#: shop/models.py:224 shop/models.py:315 +#: shop/models.py:230 shop/models.py:321 msgid "impuestos" msgstr "impuestos" -#: shop/models.py:233 +#: shop/models.py:239 msgid "documento de identidad" msgstr "documento de identidad" -#: shop/models.py:237 web/models.py:59 +#: shop/models.py:243 web/models.py:59 msgid "e-mail de contacto" msgstr "e-mail de contacto" -#: shop/models.py:243 +#: shop/models.py:249 msgid "Teléfono de contacto" msgstr "Teléfono de contacto" -#: shop/models.py:246 shop/models.py:383 shop/models.py:467 shop/models.py:470 +#: shop/models.py:252 shop/models.py:414 shop/models.py:522 shop/models.py:525 msgid "dirección" msgstr "dirección" -#: shop/models.py:247 shop/models.py:386 +#: shop/models.py:253 shop/models.py:417 msgid "ciudad" msgstr "ciudad" -#: shop/models.py:248 shop/models.py:389 +#: shop/models.py:254 shop/models.py:420 msgid "región" msgstr "región" -#: shop/models.py:249 shop/models.py:392 shop/models.py:486 +#: shop/models.py:255 shop/models.py:423 shop/models.py:541 msgid "país" msgstr "país" -#: shop/models.py:250 shop/models.py:395 shop/models.py:476 +#: shop/models.py:256 shop/models.py:426 shop/models.py:531 msgid "código postal" msgstr "código postal" -#: shop/models.py:257 +#: shop/models.py:263 msgid "proveedores" msgstr "proveedores" -#: shop/models.py:267 +#: shop/models.py:273 msgid "marcas" msgstr "marcas" -#: shop/models.py:275 shop/models.py:412 +#: shop/models.py:281 shop/models.py:466 shop/models.py:610 msgid "pedido" msgstr "pedido" -#: shop/models.py:305 +#: shop/models.py:311 msgid "total sin impuestos" msgstr "total sin impuestos" -#: shop/models.py:310 +#: shop/models.py:316 msgid "valor de impuestos" msgstr "valor de impuestos" -#: shop/models.py:320 shop/models.py:356 +#: shop/models.py:326 shop/models.py:387 msgid "total" msgstr "total" -#: shop/models.py:327 +#: shop/models.py:333 msgid "línea de pedido" msgstr "línea de pedido" -#: shop/models.py:328 +#: shop/models.py:334 msgid "líneas de pedido" msgstr "líneas de pedido" -#: shop/models.py:333 +#: shop/models.py:350 msgid "pendiente de pago" msgstr "pendiente de pago" -#: shop/models.py:334 +#: shop/models.py:351 msgid "pagado" msgstr "pagado" -#: shop/models.py:335 -msgid "preparado" -msgstr "preparado" - -#: shop/models.py:336 -msgid "listo para ser enviado" -msgstr "listo para ser enviado" - -#: shop/models.py:337 -msgid "enviado" -msgstr "enviado" - -#: shop/models.py:338 -msgid "entregado" -msgstr "entregado" - -#: shop/models.py:339 -msgid "finalizado" -msgstr "finalizado" - -#: shop/models.py:340 -msgid "cancelado" -msgstr "cancelado" - -#: shop/models.py:341 +#: shop/models.py:352 shop/models.py:364 msgid "devuelto" msgstr "devuelto" -#: shop/models.py:343 +#: shop/models.py:353 web/templates/web/orders.html:78 +msgid "Error" +msgstr "Error" + +#: shop/models.py:356 +#, fuzzy +#| msgid "preparado" +msgid "no preparado" +msgstr "no preparado" + +#: shop/models.py:357 +msgid "preparado" +msgstr "preparado" + +#: shop/models.py:358 +msgid "listo para ser enviado" +msgstr "listo para ser enviado" + +#: shop/models.py:359 +msgid "enviado" +msgstr "enviado" + +#: shop/models.py:360 +msgid "entregado" +msgstr "entregado" + +#: shop/models.py:361 +msgid "finalizado" +msgstr "finalizado" + +#: shop/models.py:362 +msgid "cancelado" +msgstr "cancelado" + +#: shop/models.py:363 +msgid "devolución solicitada" +msgstr "devolución solicitada" + +#: shop/models.py:366 msgid "UUID" msgstr "UUID" -#: shop/models.py:346 +#: shop/models.py:370 msgid "estado" msgstr "estado" -#: shop/models.py:353 +#: shop/models.py:376 +#, fuzzy +#| msgid "Direcciones de envío" +msgid "estado de envío" +msgstr "estado de envío" + +#: shop/models.py:383 msgid "base imponible" msgstr "base imponible" -#: shop/models.py:360 +#: shop/models.py:391 shop/models.py:614 msgid "cliente" msgstr "cliente" -#: shop/models.py:362 shop/models.py:498 +#: shop/models.py:393 shop/models.py:553 msgid "e-mail" msgstr "e-mail" -#: shop/models.py:366 +#: shop/models.py:397 msgid "dirección de facturación" msgstr "dirección de facturación" -#: shop/models.py:369 +#: shop/models.py:400 msgid "ciudad de facturación" msgstr "ciudad de facturación" -#: shop/models.py:372 +#: shop/models.py:403 msgid "región de facturación" msgstr "región de facturación" -#: shop/models.py:375 +#: shop/models.py:406 msgid "país de facturación" msgstr "país de facturación" -#: shop/models.py:378 +#: shop/models.py:409 msgid "código postal de facturación" msgstr "código postal de facturación" -#: shop/models.py:398 +#: shop/models.py:429 msgid "teléfono de contacto" msgstr "teléfono de contacto" -#: shop/models.py:413 -msgid "pedidos" -msgstr "pedidos" - -#: shop/models.py:417 -msgid "uuid" -msgstr "uuid" - -#: shop/models.py:421 shop/models.py:457 shop/models.py:527 -msgid "usuario" -msgstr "usuario" - -#: shop/models.py:430 shop/models.py:439 +#: shop/models.py:436 #, fuzzy -#| msgid "Carrito" -msgid "carrito" -msgstr "carrito" +#| msgid "Cantidad a pagar" +msgid "cantidad pagada" +msgstr "cantidad pagada" -#: shop/models.py:431 -#, fuzzy -#| msgid "Carrito" -msgid "carritos" -msgstr "carritos" - -#: shop/models.py:447 -#, fuzzy -#| msgid "línea de pedido" -msgid "línea de carrito" -msgstr "línea de carrito" - -#: shop/models.py:448 -#, fuzzy -#| msgid "líneas de pedido" -msgid "líneas de carrito" -msgstr "líneas de carrito" - -#: shop/models.py:453 -#, fuzzy -#| msgid "país de facturación" -msgid "facturación" -msgstr "facturación" - -#: shop/models.py:454 -msgid "envío" -msgstr "envío" - -#: shop/models.py:464 -msgid "número de identificación fiscal" -msgstr "número de identificación fiscal" - -#: shop/models.py:473 -#, fuzzy -#| msgid "cantidad" -msgid "localidad" -msgstr "localidad" - -#: shop/models.py:479 -msgid "provincia" -msgstr "provincia" - -#: shop/models.py:489 -#, fuzzy -#| msgid "teléfono de contacto" -msgid "teléfono" -msgstr "teléfono" - -#: shop/models.py:496 -#, fuzzy -#| msgid "dirección" -msgid "tipo de dirección" -msgstr "tipo de dirección" - -#: shop/models.py:499 -msgid "por defecto" -msgstr "por defecto" - -#: shop/models.py:506 -#, fuzzy -#| msgid "dirección de facturación" -msgid "dirección de cliente" -msgstr "dirección de cliente" - -#: shop/models.py:507 -#, fuzzy -#| msgid "Direcciones de envío" -msgid "direcciones de cliente" -msgstr "Direcciones de envío" - -#: shop/models.py:513 -#, fuzzy -#| msgid "descripción" -msgid "description" -msgstr "descripción" - -#: shop/models.py:515 -msgid "url" -msgstr "url" - -#: shop/models.py:517 -#, fuzzy -#| msgid "Direcciones de envío" -msgid "producto de envío" -msgstr "producto de envío" - -#: shop/models.py:519 -msgid "habilitado" -msgstr "habilitado" - -#: shop/models.py:522 +#: shop/models.py:444 shop/models.py:577 #, fuzzy #| msgid "Métodos de pago" msgid "método de envío" msgstr "método de envío" -#: shop/models.py:523 +#: shop/models.py:452 +msgid "carrito de origen de pedido" +msgstr "carrito de origen de pedido" + +#: shop/models.py:467 +msgid "pedidos" +msgstr "pedidos" + +#: shop/models.py:472 +msgid "uuid" +msgstr "uuid" + +#: shop/models.py:476 shop/models.py:512 shop/models.py:582 +msgid "usuario" +msgstr "usuario" + +#: shop/models.py:485 shop/models.py:494 +#, fuzzy +#| msgid "Carrito" +msgid "carrito" +msgstr "carrito" + +#: shop/models.py:486 +#, fuzzy +#| msgid "Carrito" +msgid "carritos" +msgstr "carritos" + +#: shop/models.py:502 +#, fuzzy +#| msgid "línea de pedido" +msgid "línea de carrito" +msgstr "línea de carrito" + +#: shop/models.py:503 +#, fuzzy +#| msgid "líneas de pedido" +msgid "líneas de carrito" +msgstr "líneas de carrito" + +#: shop/models.py:508 +#, fuzzy +#| msgid "país de facturación" +msgid "facturación" +msgstr "facturación" + +#: shop/models.py:509 +msgid "envío" +msgstr "envío" + +#: shop/models.py:519 +msgid "número de identificación fiscal" +msgstr "número de identificación fiscal" + +#: shop/models.py:528 +#, fuzzy +#| msgid "cantidad" +msgid "localidad" +msgstr "localidad" + +#: shop/models.py:534 +msgid "provincia" +msgstr "provincia" + +#: shop/models.py:544 +#, fuzzy +#| msgid "teléfono de contacto" +msgid "teléfono" +msgstr "teléfono" + +#: shop/models.py:551 +#, fuzzy +#| msgid "dirección" +msgid "tipo de dirección" +msgstr "tipo de dirección" + +#: shop/models.py:554 +msgid "por defecto" +msgstr "por defecto" + +#: shop/models.py:561 +#, fuzzy +#| msgid "dirección de facturación" +msgid "dirección de cliente" +msgstr "dirección de cliente" + +#: shop/models.py:562 +#, fuzzy +#| msgid "Direcciones de envío" +msgid "direcciones de cliente" +msgstr "Direcciones de envío" + +#: shop/models.py:568 +#, fuzzy +#| msgid "descripción" +msgid "description" +msgstr "descripción" + +#: shop/models.py:570 +msgid "url" +msgstr "url" + +#: shop/models.py:572 +#, fuzzy +#| msgid "Direcciones de envío" +msgid "producto de envío" +msgstr "producto de envío" + +#: shop/models.py:574 +msgid "habilitado" +msgstr "habilitado" + +#: shop/models.py:578 #, fuzzy #| msgid "Métodos de pago" msgid "métodos de envío" msgstr "métodos de envío" -#: shop/models.py:533 shop/models.py:534 +#: shop/models.py:588 shop/models.py:589 #, fuzzy #| msgid "productos" msgid "productos deseados" msgstr "productos deseados" -#: tpv/forms.py:7 -msgid "Introduce tu e-mail" -msgstr "Introduce tu e-mail" - -#: tpv/models.py:14 -msgid "Pagado" -msgstr "Pagado" - -#: tpv/models.py:15 -msgid "Pendiente" -msgstr "Pendiente" - -#: tpv/models.py:16 -msgid "Error" -msgstr "Error" - -#: tpv/models.py:18 +#: shop/models.py:600 msgid "Hash" msgstr "Hash" -#: tpv/models.py:24 -msgid "Estado" -msgstr "Estado" +#: shop/models.py:605 web/templates/components/cart/cart.html:23 +#: web/templates/components/cart/cart_navbar.html:40 +#, fuzzy +#| msgid "cantidad" +msgid "Cantidad" +msgstr "Cantidad" -#: tpv/models.py:30 -msgid "Cantidad a pagar" -msgstr "Cantidad a pagar" - -#: tpv/models.py:34 +#: shop/models.py:618 msgid "Fecha de creación" msgstr "Fecha de creación" -#: tpv/models.py:37 -msgid "Fecha de última modificación" -msgstr "Fecha de última modificación" - -#: tpv/models.py:41 -msgid "Email de contacto" -msgstr "Email de contacto" - -#: tpv/models.py:48 -msgid "Observaciones" -msgstr "Observaciones" - -#: tpv/models.py:52 +#: shop/models.py:621 msgid "Metadata" msgstr "Metadata" -#: tpv/models.py:71 -msgid "Pago de pedido en Redsys" -msgstr "Pago de pedido en Redsys" +#: shop/models.py:627 +#, fuzzy +#| msgid "Métodos de pago" +msgid "método de pago" +msgstr "método de pago" -#: tpv/models.py:72 -msgid "Pagos de pedidos en Redsys" -msgstr "Pagos de pedidos en Redsys" +#: shop/models.py:631 +msgid "Pago" +msgstr "Pago" -#: tpv/settings.py:230 tpv/utils.py:87 +#: shop/models.py:632 +msgid "Pagos" +msgstr "Pagos" + +#: shop/models.py:640 +#, fuzzy +#| msgid "dirección de facturación" +msgid "Identificación de comercio" +msgstr "Identificación de comercio" + +#: shop/models.py:646 +#, fuzzy +#| msgid "código de impuesto" +msgid "Código de moneda" +msgstr "Código de moneda" + +#: shop/models.py:648 +#, fuzzy +#| msgid "pedidos" +msgid "Terminal" +msgstr "Terminal" + +#: shop/models.py:649 +#, fuzzy +#| msgid "Pago de pedido en Redsys" +msgid "Clave de Redsys" +msgstr "Clave de Redsys" + +#: shop/models.py:651 +msgid "dominio del tpv" +msgstr "dominio del tpv" + +#: shop/models.py:655 shop/models.py:656 +#, fuzzy +#| msgid "ajustes de la web" +msgid "ajustes de la tienda" +msgstr "ajustes de la tienda" + +#: shop/settings.py:230 shop/utils.py:230 msgid "Error no tipificado" msgstr "Error no tipificado" -#: tpv/settings.py:231 +#: shop/settings.py:231 msgid "Transacción autorizada para devoluciones y confirmaciones" msgstr "Transacción autorizada para devoluciones y confirmaciones" -#: tpv/settings.py:232 +#: shop/settings.py:232 msgid "Transacción autorizada para anulaciones" msgstr "Transacción autorizada para anulaciones" -#: tpv/settings.py:233 +#: shop/settings.py:233 msgid "Tarjeta caducada" msgstr "Tarjeta caducada" -#: tpv/settings.py:234 +#: shop/settings.py:234 msgid "Tarjeta en excepción transitoria o bajo sospecha de fraude" msgstr "Tarjeta en excepción transitoria o bajo sospecha de fraude" -#: tpv/settings.py:235 +#: shop/settings.py:235 msgid "Intentos de PIN excedidos" msgstr "Intentos de PIN excedidos" -#: tpv/settings.py:236 +#: shop/settings.py:236 msgid "Tarjeta no efectiva" msgstr "Tarjeta no efectiva" -#: tpv/settings.py:237 +#: shop/settings.py:237 msgid "Código de seguridad (CVV2/CVC2) incorrecto" msgstr "Código de seguridad (CVV2/CVC2) incorrecto" -#: tpv/settings.py:238 +#: shop/settings.py:238 msgid "Denegada, no repetir." msgstr "Denegada, no repetir." -#: tpv/settings.py:239 +#: shop/settings.py:239 msgid "Denegada, no repetir sin actualizar datos de tarjeta." msgstr "Denegada, no repetir sin actualizar datos de tarjeta." -#: tpv/settings.py:240 +#: shop/settings.py:240 msgid "Denegada, no repetir antes de 72 horas." msgstr "Denegada, no repetir antes de 72 horas." -#: tpv/settings.py:241 +#: shop/settings.py:241 msgid "Tarjeta ajena al servicio" msgstr "Tarjeta ajena al servicio" -#: tpv/settings.py:242 +#: shop/settings.py:242 msgid "Error en la autenticación del titular" msgstr "Error en la autenticación del titular" -#: tpv/settings.py:243 +#: shop/settings.py:243 msgid "Denegación del emisor sin especificar motivo" msgstr "Denegación del emisor sin especificar motivo" -#: tpv/settings.py:244 +#: shop/settings.py:244 msgid "Fecha de caducidad errónea" msgstr "Fecha de caducidad errónea" -#: tpv/settings.py:245 +#: shop/settings.py:245 msgid "Requiere autenticación SCA" msgstr "Requiere autenticación SCA" -#: tpv/settings.py:247 +#: shop/settings.py:247 msgid "" "Tarjeta en excepción transitoria o bajo sospecha de fraude con retirada de " "tarjeta" @@ -590,67 +637,67 @@ msgstr "" "Tarjeta en excepción transitoria o bajo sospecha de fraude con retirada de " "tarjeta" -#: tpv/settings.py:249 +#: shop/settings.py:249 msgid "Comercio no registrado en FUC" msgstr "Comercio no registrado en FUC" -#: tpv/settings.py:250 +#: shop/settings.py:250 msgid "Error de sistema" msgstr "Error de sistema" -#: tpv/settings.py:251 +#: shop/settings.py:251 msgid "Pedido repetido" msgstr "Pedido repetido" -#: tpv/settings.py:252 +#: shop/settings.py:252 msgid "Sesión Incorrecta" msgstr "Sesión Incorrecta" -#: tpv/settings.py:253 +#: shop/settings.py:253 msgid "Operación de devolución no permitida" msgstr "Operación de devolución no permitida" -#: tpv/settings.py:254 tpv/settings.py:255 +#: shop/settings.py:254 shop/settings.py:255 msgid "Emisor no disponible" msgstr "" -#: tpv/settings.py:256 +#: shop/settings.py:256 msgid "Número de posiciones de la tarjeta incorrecto" msgstr "Número de posiciones de la tarjeta incorrecto" -#: tpv/settings.py:257 +#: shop/settings.py:257 msgid "Tipo de operación no permitida para esa tarjeta" msgstr "Tipo de operación no permitida para esa tarjeta" -#: tpv/settings.py:258 +#: shop/settings.py:258 msgid "Tarjeta no existente" msgstr "Tarjeta no existente" -#: tpv/settings.py:259 +#: shop/settings.py:259 msgid "Rechazo servidores internacionales" msgstr "Rechazo servidores internacionales" -#: tpv/settings.py:260 +#: shop/settings.py:260 msgid "Comercio con titular seguro y titular sin clave de compra segura" msgstr "Comercio con titular seguro y titular sin clave de compra segura" -#: tpv/settings.py:261 +#: shop/settings.py:261 msgid "El comercio no permite op. seguras por entrada /operaciones" msgstr "El comercio no permite op. seguras por entrada /operaciones" -#: tpv/settings.py:262 +#: shop/settings.py:262 msgid "Tarjeta no cumple el check-digit" msgstr "Tarjeta no cumple el check-digit" -#: tpv/settings.py:263 +#: shop/settings.py:263 msgid "El comercio no puede realizar preautorizaciones" msgstr "El comercio no puede realizar preautorizaciones" -#: tpv/settings.py:264 +#: shop/settings.py:264 msgid "Esta tarjeta no permite operativa de preautorizaciones" msgstr "Esta tarjeta no permite operativa de preautorizaciones" -#: tpv/settings.py:266 +#: shop/settings.py:266 msgid "" "Operación detenida por superar el control de restricciones en la entrada al " "SIS" @@ -658,27 +705,27 @@ msgstr "" "Operación detenida por superar el control de restricciones en la entrada al " "SIS" -#: tpv/settings.py:268 +#: shop/settings.py:268 msgid "A petición del usuario se ha cancelado el pago" msgstr "A petición del usuario se ha cancelado el pago" -#: tpv/settings.py:269 +#: shop/settings.py:269 msgid "Se está procesando otra transacción en SIS con la misma tarjeta" msgstr "Se está procesando otra transacción en SIS con la misma tarjeta" -#: tpv/settings.py:270 +#: shop/settings.py:270 msgid "Operación en proceso de solicitud de datos de tarjeta" msgstr "Operación en proceso de solicitud de datos de tarjeta" -#: tpv/settings.py:271 +#: shop/settings.py:271 msgid "Operación que ha sido redirigida al emisor a autenticar" msgstr "Operación que ha sido redirigida al emisor a autenticar" -#: tpv/utils.py:75 +#: shop/utils.py:218 msgid "No se ha recibido ningún valor para Ds_MerchantParameters" msgstr "No se ha recibido ningún valor para Ds_MerchantParameters" -#: tpv/utils.py:89 +#: shop/utils.py:232 #, python-brace-format msgid "No se ha realizado el pago. Motivo: {reason}" msgstr "No se ha realizado el pago. Motivo: {reason}" @@ -687,10 +734,14 @@ msgstr "No se ha realizado el pago. Motivo: {reason}" msgid "Contraseña" msgstr "Contraseña" -#: users/forms/login.py:30 +#: users/forms/login.py:31 msgid "Mantener sesión iniciada" msgstr "Mantener sesión iniciada" +#: users/forms/reset_password.py:16 users/forms/reset_password.py:30 +msgid "El token no es válido o ha expirado" +msgstr "El token no es válido o ha expirado" + #: web/models.py:9 msgid "título de la web" msgstr "título de la web" @@ -775,7 +826,7 @@ msgstr "logo 128x128" msgid "ajustes de la web" msgstr "ajustes de la web" -#: web/templates/components/cart/add_to_cart.html:12 +#: web/templates/components/cart/add_to_cart.html:17 msgid "Añadir al carrito" msgstr "Añadir al carrito" @@ -783,30 +834,21 @@ msgstr "Añadir al carrito" msgid "Carrito" msgstr "Carrito" -#: web/templates/components/cart/cart.html:23 -#: web/templates/components/cart/cart_navbar.html:40 -#: web/templates/web/order.html:30 -#, fuzzy -#| msgid "cantidad" -msgid "Cantidad" -msgstr "Cantidad" - #: web/templates/components/cart/cart.html:54 #: web/templates/components/cart/cart_navbar.html:47 -#: web/templates/web/order.html:61 msgid "Quitar" msgstr "Quitar" -#: web/templates/components/cart/cart.html:66 web/templates/web/order.html:73 +#: web/templates/components/cart/cart.html:66 web/templates/web/order.html:62 msgid "No hay nada en el carrito..." msgstr "No hay nada en el carrito..." -#: web/templates/components/cart/cart.html:71 web/templates/web/order.html:78 +#: web/templates/components/cart/cart.html:71 web/templates/web/order.html:67 msgid "Volver a inicio" msgstr "Volver a inicio" #: web/templates/components/cart/cart.html:77 -#: web/templates/users/user_details.html:67 web/templates/web/order.html:85 +#: web/templates/users/user_details.html:67 web/templates/web/order.html:74 #, fuzzy #| msgid "Direcciones de envío" msgid "Dirección de envío" @@ -871,48 +913,49 @@ msgid "País" msgstr "País" #: web/templates/components/cart/cart.html:176 -#: web/templates/users/user_details.html:65 web/templates/web/order.html:99 +#: web/templates/users/user_details.html:65 web/templates/web/order.html:89 #, fuzzy #| msgid "dirección de facturación" msgid "Dirección de facturación" msgstr "Dirección de facturación" -#: web/templates/components/cart/cart.html:260 web/templates/web/order.html:113 +#: web/templates/components/cart/cart.html:260 #, fuzzy #| msgid "Direcciones de envío" msgid "Opciones de envío" msgstr "Opciones de envío" -#: web/templates/components/cart/cart.html:288 web/templates/web/order.html:12 -#: web/templates/web/order.html:132 +#: web/templates/components/cart/cart.html:288 web/templates/web/order.html:10 +#: web/templates/web/order.html:119 #, fuzzy #| msgid "líneas de pedido" msgid "Resumen del pedido" msgstr "Resumen del pedido" -#: web/templates/components/cart/cart.html:294 web/templates/web/order.html:138 +#: web/templates/components/cart/cart.html:294 web/templates/web/order.html:126 #, fuzzy #| msgid "base imponible" msgid "Base imponible" msgstr "Base imponible" -#: web/templates/components/cart/cart.html:299 web/templates/web/order.html:143 +#: web/templates/components/cart/cart.html:299 web/templates/web/order.html:134 msgid "Descuentos" msgstr "Descuentos" -#: web/templates/components/cart/cart.html:305 web/templates/web/order.html:149 +#: web/templates/components/cart/cart.html:305 web/templates/web/order.html:142 #, fuzzy #| msgid "Direcciones de envío" msgid "Gastos de envío" msgstr "Gastos de envío" -#: web/templates/components/cart/cart.html:310 web/templates/web/order.html:154 +#: web/templates/components/cart/cart.html:310 web/templates/web/order.html:151 #, fuzzy #| msgid "impuestos" msgid "Impuestos" msgstr "Impuestos" -#: web/templates/components/cart/cart.html:316 web/templates/web/order.html:160 +#: web/templates/components/cart/cart.html:316 web/templates/web/order.html:159 +#: web/templates/web/orders.html:40 #, fuzzy #| msgid "total" msgid "Total" @@ -924,11 +967,11 @@ msgstr "Total" msgid "Terminar pedido" msgstr "Terminar pedido" -#: web/templates/components/cart/cart.html:327 web/templates/web/order.html:171 +#: web/templates/components/cart/cart.html:327 web/templates/web/order.html:177 msgid "o" msgstr "o" -#: web/templates/components/cart/cart.html:330 web/templates/web/order.html:174 +#: web/templates/components/cart/cart.html:330 web/templates/web/order.html:180 msgid "Seguir comprando" msgstr "Seguir comprando" @@ -948,18 +991,14 @@ msgstr "" msgid "Quitar del carrito" msgstr "Quitar del carrito" -#: web/templates/components/cart/cart_navbar.html:71 +#: web/templates/components/cart/cart_navbar.html:69 #, fuzzy #| msgid "Carrito" msgid "Ver carrito" msgstr "Ver carrito" -#: web/templates/components/cart/cart_navbar.html:77 -msgid "Comprar" -msgstr "Comprar" - #: web/templates/components/generic/navbar.html:22 -#: web/templates/components/generic/navbar.html:87 +#: web/templates/components/generic/navbar.html:88 msgid "Inicio" msgstr "Inicio" @@ -969,7 +1008,7 @@ msgstr "Inicio" msgid "Mi cuenta" msgstr "Mi cuenta" -#: web/templates/components/generic/navbar.html:75 +#: web/templates/components/generic/navbar.html:76 msgid "Abrir menú" msgstr "Abrir menú" @@ -979,51 +1018,109 @@ msgid "Buscar" msgstr "Buscar" #: web/templates/components/users/user_dropdown.html:12 +#: web/templates/web/orders.html:10 #, fuzzy #| msgid "pedidos" msgid "Mis pedidos" msgstr "Mis pedidos" #: web/templates/components/users/user_dropdown.html:18 -msgid "Ajustes" -msgstr "Ajustes" - -#: web/templates/components/users/user_dropdown.html:24 #: web/templates/web/wishlist.html:17 #, fuzzy #| msgid "Añadir a lista de deseados" msgid "Lista de deseados" msgstr "Lista de deseados" -#: web/templates/components/users/user_dropdown.html:30 -msgid "Direcciones de envío" -msgstr "Direcciones de envío" - -#: web/templates/components/users/user_dropdown.html:36 -msgid "Métodos de pago" -msgstr "Métodos de pago" - -#: web/templates/components/users/user_dropdown.html:44 +#: web/templates/components/users/user_dropdown.html:26 msgid "Cerrar sesión" msgstr "Cerrar sesión" -#: web/templates/users/login.html:17 +#: web/templates/mails/reset_password.html:2 +#, python-format +msgid "" +"You're receiving this email because you requested a password reset for your " +"user account at %(site_name)s." +msgstr "" +"Ha recibido este correo electrónico porque ha solicitado restablecer la contraseña para su " +"cuenta en %(site_name)s." + +#: web/templates/mails/reset_password.html:4 +msgid "Please go to the following page and choose a new password:" +msgstr "Por favor, vaya a la página siguiente y escoja una nueva contraseña:" + +#: web/templates/mails/reset_password.html:8 +msgid "Your username, in case you’ve forgotten:" +msgstr "Su nombre de usuario, en caso de que lo haya olvidado:" + +#: web/templates/mails/reset_password.html:10 +msgid "Thanks for using our site!" +msgstr " ¡Gracias por usar nuestro sitio!" + +#: web/templates/mails/reset_password.html:12 +#, python-format +msgid "The %(site_name)s team" +msgstr "El equipo de %(site_name)" + +#: web/templates/users/login.html:18 msgid "Inicia sesión en tu cuenta" msgstr "Inicia sesión en tu cuenta" -#: web/templates/users/login.html:43 +#: web/templates/users/login.html:45 web/templates/users/reset_password.html:37 +#: web/templates/users/reset_password_confirm.html:38 +#: web/templates/users/reset_password_email_sent.html:22 +msgid "¿Aún no tienes cuenta?" +msgstr "¿Aún no tienes cuenta?" + +#: web/templates/users/login.html:47 web/templates/users/reset_password.html:38 +#: web/templates/users/reset_password_confirm.html:39 +#: web/templates/users/reset_password_email_sent.html:24 +msgid "Regístrate aquí" +msgstr "Regístrate aquí" + +#: web/templates/users/login.html:53 #, fuzzy #| msgid "Cerrar sesión" msgid "Iniciar sesión" msgstr "Iniciar sesión" -#: web/templates/users/login.html:46 -msgid "¿Aún no tienes cuenta?" -msgstr "¿Aún no tienes cuenta?" +#: web/templates/users/login.html:59 +msgid "¿Has olvidado tu contraseña?" +msgstr "¿Has olvidado tu contraseña?" -#: web/templates/users/login.html:47 -msgid "Regístrate aquí" -msgstr "Regístrate aquí" +#: web/templates/users/register.html:17 +#, fuzzy +#| msgid "Mi cuenta" +msgid "Crea tu cuenta" +msgstr "Crea tu cuenta" + +#: web/templates/users/register.html:42 web/views/users.py:109 +#, fuzzy +#| msgid "Mi cuenta" +msgid "Crear cuenta" +msgstr "Crear cuenta" + +#: web/templates/users/register.html:45 +#, fuzzy +#| msgid "¿Aún no tienes cuenta?" +msgid "¿Ya tienes cuenta?" +msgstr "¿Ya tienes cuenta?" + +#: web/templates/users/register.html:46 +#, fuzzy +#| msgid "Cerrar sesión" +msgid "Inicia sesión aquí" +msgstr "Iniciar sesión aquí" + +#: web/templates/users/reset_password.html:17 +#: web/templates/users/reset_password.html:34 +#: web/templates/users/reset_password_confirm.html:17 +#: web/templates/users/reset_password_confirm.html:35 web/views/users.py:149 +msgid "Restablecer contraseña" +msgstr "Restablecer contraseña" + +#: web/templates/users/reset_password_email_sent.html:18 +msgid "Se te ha enviado un correo electrónico para restablecer tu contraseña" +msgstr "Se te ha enviado un correo electrónico para restablecer tu contraseña" #: web/templates/users/user_details.html:24 #, fuzzy @@ -1031,6 +1128,12 @@ msgstr "Regístrate aquí" msgid "Mis direcciones" msgstr "Mis direcciones" +#: web/templates/users/user_details.html:32 +#, fuzzy +#| msgid "dirección" +msgid "No hay ninguna dirección" +msgstr "tipo de dirección" + #: web/templates/users/user_details.html:56 #, fuzzy #| msgid "Teléfono de contacto" @@ -1041,71 +1144,191 @@ msgstr "Teléfono" msgid "Editar datos" msgstr "Editar datos" -#: web/templates/web/list_products.html:22 +#: web/templates/web/list_products.html:24 msgid "Anterior" msgstr "Anterior" -#: web/templates/web/list_products.html:36 +#: web/templates/web/list_products.html:38 #, fuzzy #| msgid "cliente" msgid "Siguiente" msgstr "Siguiente" -#: web/templates/web/order.html:167 +#: web/templates/web/order.html:13 web/templates/web/orders.html:51 +msgid "Pagado" +msgstr "Pagado" + +#: web/templates/web/order.html:104 +#, fuzzy +#| msgid "Métodos de pago" +msgid "Método de envío" +msgstr "método de envío" + +#: web/templates/web/order.html:173 #, fuzzy #| msgid "Pagado" msgid "Pagar" msgstr "Pagar" -#: web/urls.py:31 +#: web/templates/web/orders.html:20 +#, fuzzy +#| msgid "línea de pedido" +msgid "Número de pedido" +msgstr "Número de pedido" + +#: web/templates/web/orders.html:31 +#, fuzzy +#| msgid "fecha" +msgid "Fecha" +msgstr "Fecha" + +#: web/templates/web/orders.html:56 +#, fuzzy +#| msgid "habilitado" +msgid "Tramitado" +msgstr "Tramitado" + +#: web/templates/web/orders.html:60 +#, fuzzy +#| msgid "preparado" +msgid "Preparado" +msgstr "Preparado" + +#: web/templates/web/orders.html:64 +msgid "Listo" +msgstr "Listo" + +#: web/templates/web/orders.html:68 +#, fuzzy +#| msgid "enviado" +msgid "Enviado" +msgstr "Enviado" + +#: web/templates/web/orders.html:72 +#, fuzzy +#| msgid "entregado" +msgid "Entregado" +msgstr "Entregado" + +#: web/templates/web/orders.html:82 +#, fuzzy +#| msgid "devuelto" +msgid "Devuelto" +msgstr "Devuelto" + +#: web/templates/web/orders.html:89 +#, fuzzy +#| msgid "pedido" +msgid "Ver pedido" +msgstr "Ver pedido" + +#: web/urls.py:41 msgid "wishlist" msgstr "lista-deseados" -#: web/urls.py:32 +#: web/urls.py:42 msgid "categories//" msgstr "categorias//" -#: web/urls.py:33 +#: web/urls.py:43 msgid "products///" msgstr "productos///" -#: web/urls.py:34 +#: web/urls.py:44 msgid "cart/" msgstr "carrito/" -#: web/urls.py:35 +#: web/urls.py:45 +msgid "orders/" +msgstr "pedidos/" + +#: web/urls.py:46 msgid "order//" msgstr "pedido//" -#: web/urls.py:37 +#: web/urls.py:48 msgid "login/" msgstr "iniciar-sesion/" -#: web/urls.py:38 +#: web/urls.py:49 msgid "logout/" msgstr "cerrar-sesion/" -#: web/urls.py:39 +#: web/urls.py:50 msgid "register/" msgstr "crear-cuenta/" -#: web/urls.py:40 +#: web/urls.py:51 #, fuzzy #| msgid "Password" msgid "reset-password/" msgstr "restablecer-contrasena/" -#: web/urls.py:41 +#: web/urls.py:53 +msgid "reset///" +msgstr "restablecer-contrasena///" + +#: web/urls.py:58 +#, fuzzy +#| msgid "Password" +msgid "reset-password/email-sent/" +msgstr "restablecer-contrasena/email-enviado/" + +#: web/urls.py:62 msgid "my-account/" msgstr "mi-cuenta/" -#: web/views/users.py:40 +#: web/views/users.py:48 msgid "inicio de sesión" -msgstr "" +msgstr "inicio de sesión" -#: web/views/web.py:82 +#: web/views/users.py:77 +#, fuzzy +#| msgid "Mi cuenta" +msgid "crear cuenta" +msgstr "crear cuenta" + +#: web/views/users.py:100 +msgid "Ya existe un usuario con ese correo electrónico" +msgstr "Ya existe un usuario con ese correo electrónico" + +#: web/views/users.py:212 +msgid "Establecer nueva contraseña" +msgstr "Establecer nueva contraseña" + +#: web/views/web.py:86 msgid "resumen del carrito" -msgstr "" +msgstr "resumen del carrito" + +#~ msgid "Introduce tu e-mail" +#~ msgstr "Introduce tu e-mail" + +#~ msgid "Pendiente" +#~ msgstr "Pendiente" + +#~ msgid "Estado" +#~ msgstr "Estado" + +#~ msgid "Fecha de última modificación" +#~ msgstr "Fecha de última modificación" + +#~ msgid "Email de contacto" +#~ msgstr "Email de contacto" + +#~ msgid "Observaciones" +#~ msgstr "Observaciones" + +#~ msgid "Pagos de pedidos en Redsys" +#~ msgstr "Pagos de pedidos en Redsys" + +#~ msgid "Comprar" +#~ msgstr "Comprar" + +#~ msgid "Ajustes" +#~ msgstr "Ajustes" + +#~ msgid "Direcciones de envío" +#~ msgstr "Direcciones de envío" #~ msgid "apellidos" #~ msgstr "apellidos" diff --git a/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py b/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py index 61b291f..3061fd5 100644 --- a/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py +++ b/shop/migrations/0005_alter_order_options_order_amount_paid_and_more.py @@ -1,7 +1,8 @@ # Generated by Django 5.1.4 on 2025-01-16 17:00 -import django.db.models.deletion from decimal import Decimal + +import django.db.models.deletion from django.db import migrations, models diff --git a/shop/utils.py b/shop/utils.py index 13bea48..b7c2bee 100644 --- a/shop/utils.py +++ b/shop/utils.py @@ -14,6 +14,7 @@ from django.utils.text import gettext_lazy as _ from shop.exceptions import RedsysPaymentException, RedsysValidationException from shop.models import ( Cart, + CartItem, Order, OrderLine, Payment, @@ -21,7 +22,6 @@ from shop.models import ( ProductBatch, ProductPrice, ShippingMethod, - CartItem, ) from shop.settings import ERROR_CODES from shop.signals import clear_cart diff --git a/shop/views.py b/shop/views.py index b48ecf8..fd518ef 100644 --- a/shop/views.py +++ b/shop/views.py @@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404 from django.views.decorators.csrf import csrf_exempt from shop.models import Order -from shop.utils import validate_payment_for_order, add_payment_to_order +from shop.utils import add_payment_to_order, validate_payment_for_order @csrf_exempt diff --git a/users/forms/register.py b/users/forms/register.py index e197683..bf3cc74 100644 --- a/users/forms/register.py +++ b/users/forms/register.py @@ -1,5 +1,5 @@ from django import forms -from django.contrib.auth.forms import SetPasswordMixin, BaseUserCreationForm +from django.contrib.auth.forms import BaseUserCreationForm, SetPasswordMixin from django.contrib.auth.models import User from django.forms import EmailField diff --git a/users/forms/reset_password.py b/users/forms/reset_password.py index e69de29..cb5c1a9 100644 --- a/users/forms/reset_password.py +++ b/users/forms/reset_password.py @@ -0,0 +1,38 @@ +from django import forms +from django.contrib.auth.forms import PasswordResetForm as BasePasswordResetForm +from django.contrib.auth.forms import SetPasswordForm as BaseSetPasswordForm +from django.contrib.auth.tokens import default_token_generator +from django.core.exceptions import ValidationError + + +class PasswordResetForm(BasePasswordResetForm): + pass + + +def validate_token(token): + is_valid = default_token_generator.check_token(token) + + if not is_valid: + raise ValidationError(_("El token no es válido o ha expirado")) + + +class SetPasswordForm(BaseSetPasswordForm): + token = forms.CharField( + required=True, + widget=forms.HiddenInput(), + validators=[], + ) + + def validate_token(self): + is_valid = default_token_generator.check_token(self.user, self.data.get('token')) + + if not is_valid: + raise ValidationError(_("El token no es válido o ha expirado")) + + def is_valid(self): + try: + self.validate_token() + except ValidationError as e: + self.add_error(self.token, str(e)) + return False + return super().is_valid() diff --git a/web/templates/mails/reset_password.html b/web/templates/mails/reset_password.html new file mode 100644 index 0000000..46ed4ee --- /dev/null +++ b/web/templates/mails/reset_password.html @@ -0,0 +1,14 @@ +{% load i18n %}{% autoescape off %} +{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %} + +{% translate "Please go to the following page and choose a new password:" %} +{% block reset_link %} +{{ protocol }}://{{ domain }}{% url 'web:reset_password_confirm' uidb64=uid token=token %} +{% endblock %} +{% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }} + +{% translate "Thanks for using our site!" %} + +{% blocktranslate %}The {{ site_name }} team{% endblocktranslate %} + +{% endautoescape %} diff --git a/web/templates/mails/reset_password.txt b/web/templates/mails/reset_password.txt new file mode 100644 index 0000000..ba35d17 --- /dev/null +++ b/web/templates/mails/reset_password.txt @@ -0,0 +1 @@ +Hola madafaka diff --git a/web/templates/mails/subject_reset_password.txt b/web/templates/mails/subject_reset_password.txt new file mode 100644 index 0000000..e69de29 diff --git a/web/templates/users/login.html b/web/templates/users/login.html index 980dc30..95f9092 100644 --- a/web/templates/users/login.html +++ b/web/templates/users/login.html @@ -5,7 +5,8 @@
- + logo {{ title }} @@ -28,23 +29,37 @@ {{ field }} {% else %}
+ {{ field }} - {{ field }} +
{% endif %} {% endfor %} + +
+

+ {% translate '¿Aún no tienes cuenta?' %} + {% translate 'Regístrate aquí' %} +

+
+ -

- {% translate '¿Aún no tienes cuenta?' %} - {% translate 'Regístrate aquí' %} -

+ + +
diff --git a/web/templates/users/reset_password.html b/web/templates/users/reset_password.html index 34508a9..5b2da8f 100644 --- a/web/templates/users/reset_password.html +++ b/web/templates/users/reset_password.html @@ -1,42 +1,46 @@ -{% extends 'base/base.html' %} +{% extends 'theme/base.html' %} +{% load i18n %} -{% block content %} +{% block main %} +
+
-
-
- - logo + + logo {{ title }} -
-

- Change Password -

-
-
- - -
-
- - -
-
- - -
-
-
- -
-
- -
-
- -
-
-
-
-{% endblock %} \ No newline at end of file +
+
+

+ {% translate 'Restablecer contraseña' %} +

+
+ {% csrf_token %} + {% for field in form %} + +
+ + {{ field }} +
+ + {% endfor %} + +

+ {% translate '¿Aún no tienes cuenta?' %} + {% translate 'Regístrate aquí' %} +

+
+
+
+
+
+ +{% endblock %} diff --git a/web/templates/users/reset_password_confirm.html b/web/templates/users/reset_password_confirm.html new file mode 100644 index 0000000..679a05b --- /dev/null +++ b/web/templates/users/reset_password_confirm.html @@ -0,0 +1,50 @@ +{% extends 'theme/base.html' %} +{% load i18n %} + +{% block main %} +
+
+ + + logo + {{ title }} + + +
+
+

+ {% translate 'Restablecer contraseña' %} +

+ {% if valid_token %} +
+ {% csrf_token %} + {% for field in form %} + +
+ + {{ field }} +
+ + {% endfor %} + +

+ {% translate '¿Aún no tienes cuenta?' %} + {% translate 'Regístrate aquí' %} +

+
+ {% else %} + El enlace no es válido o ha expirado + {% endif %} +
+
+
+
+ +{% endblock %} diff --git a/web/templates/users/reset_password_email_sent.html b/web/templates/users/reset_password_email_sent.html new file mode 100644 index 0000000..5d5ac2b --- /dev/null +++ b/web/templates/users/reset_password_email_sent.html @@ -0,0 +1,31 @@ +{% extends 'theme/base.html' %} +{% load i18n %} + +{% block main %} +
+
+ + + logo + {{ title }} + + +
+
+

+ {% translate 'Se te ha enviado un correo electrónico para restablecer tu contraseña' %} +

+ +

+ {% translate '¿Aún no tienes cuenta?' %} + {% translate 'Regístrate aquí' %} +

+
+
+
+
+ +{% endblock %} diff --git a/web/tests/test_manifest.py b/web/tests/test_manifest.py index b7bf3cb..f6fe1dd 100644 --- a/web/tests/test_manifest.py +++ b/web/tests/test_manifest.py @@ -1,10 +1,11 @@ import os + +from django.conf import settings +from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase from django.urls import reverse -from django.conf import settings from web.models import WebSettings -from django.core.files.uploadedfile import SimpleUploadedFile class TestManifest(TestCase): diff --git a/web/tests/test_orders.py b/web/tests/test_orders.py index adbeca3..d8415f7 100644 --- a/web/tests/test_orders.py +++ b/web/tests/test_orders.py @@ -7,8 +7,8 @@ from django.urls import reverse from shop.models import ( Cart, CartItem, - Order, CustomerAddress, + Order, ShippingMethod, ShopSettings, ) diff --git a/web/urls.py b/web/urls.py index 872df3a..7ee41ea 100644 --- a/web/urls.py +++ b/web/urls.py @@ -10,7 +10,15 @@ from web.views.components import ( list_wishlisted_products, wishlist_button, ) -from web.views.users import login, logout, my_account, register, reset_password +from web.views.users import ( + login, + logout, + my_account, + register, + reset_password, + reset_password_confirm, + reset_password_email_sent, +) from web.views.web import ( add_cart_item, add_to_wishlist, @@ -40,7 +48,17 @@ i18n_resolvers = i18n_patterns( path(_("login/"), login, name="login"), path(_("logout/"), logout, name="logout"), path(_("register/"), register, name="register"), - path(_("reset-password/"), reset_password, name="reset-password"), + path(_("reset-password/"), reset_password, name="reset_password"), + path( + _("reset///"), + reset_password_confirm, + name="reset_password_confirm", + ), + path( + _("reset-password/email-sent/"), + reset_password_email_sent, + name="reset_password_email_sent", + ), path(_("my-account/"), my_account, name="my_account"), ) diff --git a/web/views/users.py b/web/views/users.py index 6f7fe35..3768bd0 100644 --- a/web/views/users.py +++ b/web/views/users.py @@ -1,16 +1,19 @@ -from django.contrib.auth import login as login_user, authenticate -from django.contrib.auth import logout as logout_user +from django.conf import settings from django.contrib.auth import get_user_model +from django.contrib.auth import login as login_user +from django.contrib.auth import logout as logout_user +from django.contrib.auth.tokens import default_token_generator +from django.core.exceptions import ValidationError from django.db import IntegrityError from django.shortcuts import redirect, render, reverse +from django.utils.http import url_has_allowed_host_and_scheme, urlsafe_base64_decode from django.utils.text import gettext_lazy as _ from django.views.generic import TemplateView, View from shop.models import CustomerAddress -from users.forms import LoginForm, RegisterForm +from users.forms import LoginForm, PasswordResetForm, RegisterForm, SetPasswordForm from web.models import WebSettings - User = get_user_model() @@ -123,7 +126,108 @@ class LogoutView(View, RedirectionMixin): class ResetPasswordView(TemplateView): - template_name = "web/reset-password.html" + template_name = "users/reset_password.html" + form_class = PasswordResetForm + redirect_to = "web:reset_password_email_sent" + email_reset_password_template_name = "mails/reset_password.txt" + html_reset_password_template_name = "mails/reset_password.html" + subject_template_name = "mails/subject_reset_password.txt" + + def get_email_options(self): + return { + "extra_email_context": {}, + "email_template_name": self.email_reset_password_template_name, + "html_email_template_name": self.html_reset_password_template_name, + "subject_template_name": self.subject_template_name, + } + + def get_base_context(self): + settings = WebSettings.load() + + return { + "title": settings.web_title, + "description": _("Restablecer contraseña"), + } + + def get_context_data(self, **kwargs): + context = self.get_base_context() + context.update({"form": self.form_class()}) + return context + + def post(self, request, *args, **kwargs): + form = self.form_class(request.POST) + + if form.is_valid(): + opts = { + "use_https": request.is_secure(), + "from_email": getattr(settings, "DEFAULT_FROM_EMAIL"), + "request": request, + } + + opts.update(self.get_email_options()) + form.save(**opts) + return redirect(self.redirect_to) + + context = self.get_base_context() + context.update({"form": form}) + + return render(request, self.template_name, context) + + +class ResetPasswordEmailSent(TemplateView): + template_name = "users/reset_password_email_sent.html" + + +class ResetPasswordConfirmView(TemplateView): + template_name = "users/reset_password_confirm.html" + form_class = SetPasswordForm + redirect_to = "web:login" + token_generator = default_token_generator + + def get_user(self): + try: + uidb64 = self.kwargs.get("uidb64") + uid = urlsafe_base64_decode(uidb64).decode() + user = User.objects.get(pk=uid) + except ( + TypeError, + ValueError, + OverflowError, + User.DoesNotExist, + ValidationError, + ): + user = None + return user + + def generate_new_token_for_user(self, user): + return self.token_generator.make_token(user) + + def get_context_data(self, **kwargs): + user = self.get_user() + valid_token = self.check_token(user) + new_token = self.generate_new_token_for_user(user) + + return { + "title": WebSettings.load().web_title, + "description": _("Establecer nueva contraseña"), + "form": self.form_class(user, {'token': new_token}), + "valid_token": valid_token, + "new_token": new_token, + } + + def check_token(self, user): + token = self.kwargs.get("token") + valid_token = self.token_generator.check_token(user, token) + return valid_token + + def post(self, request, *args, **kwargs): + user = self.get_user() + form = self.form_class(user, request.POST) + + if form.is_valid(): + form.save() + return redirect(self.redirect_to) + return render(request, self.template_name, {}) class MyAccountView(TemplateView): @@ -143,4 +247,7 @@ logout = LogoutView.as_view() register = RegisterView.as_view() reset_password = ResetPasswordView.as_view() +reset_password_confirm = ResetPasswordConfirmView.as_view() +reset_password_email_sent = ResetPasswordEmailSent.as_view() + my_account = MyAccountView.as_view() diff --git a/web/views/web.py b/web/views/web.py index 0ab8b78..5ef7786 100644 --- a/web/views/web.py +++ b/web/views/web.py @@ -19,7 +19,7 @@ from shop.models import ( from shop.redsys import RedsysClient from shop.utils import create_order_from_cart from web.forms import CreateOrderForm -from web.mixins import PaginatedQuerysetMixin, FilteredQuerysetMixin +from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin from web.models import WebSettings from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME from web.utils import get_or_create_cart